本文記錄Spring Async對Java多線程的支持
使用場景
Java在處理多線程時需要用到線程池及其相關的API,配置較爲零散,學習成本較高.Spring提供了便捷的配置類來支持多線程的實現.
技術點
- Java多線程
- Spring Async
Spring Async配置類
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 開啓多線程操作,註解方式
* 在需要進行異步處理的方法上加上@Async註解
*/
@Configuration
@EnableAsync
public class AsyncConfig {
/**
* 設置線程池基本大小值, 線程池維護線程的最少數量
*/
private int corePoolSize = 10;
/**
* 設置線程池最大值
*/
private int maxPoolSize = 200;
/**
* 線程池所使用的緩衝隊列大小
*/
private int queueCapacity = 1024;
/**
* 配置線程最大空閒時間
*/
private int keepAliveSeconds = 50000;
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix("AsyncExecutor-");
/*
* rejection-policy:當pool已經達到max size的時候,如何處理新任務
* CALLER_RUNS:不在新線程中執行任務,而是由調用者所在的線程來執行
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
Spring Async服務類-控制層
import org.eureka.service.impl.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* Description: 異步任務測試類
*/
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String testAsyncNoRetrun(){
long start = System.currentTimeMillis();
asyncService.doNoReturn();
return String.format("任務執行成功,耗時{%s}", System.currentTimeMillis() - start);
}
@GetMapping("/hi")
public Map<String, Object> testAsyncReturn() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
Map<String, Object> map = new HashMap<>();
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future<String> future = asyncService.doReturn(i);
futures.add(future);
}
/*
* 讀取的時候,記得要批量讀取不能單獨讀取,否則無法實現異步的效果
* 且需要注意:
* 異步方法調用一定需要通過Spring代理,@Async和@Transactional註解類似,只有在代理模式下生效
*/
List<String> response = new ArrayList<>();
for (Future future : futures) {
String string = (String) future.get();
response.add(string);
}
map.put("data", response);
map.put("消耗時間", String.format("任務執行成功,耗時{%s}毫秒", System.currentTimeMillis() - start));
return map;
}
}
Spring Async服務類-實現層
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.Future;
/**
* Description: 異步任務測試類
*/
@Service
public class AsyncService {
/**
* TODO 處理無返回的異步任務
* @param
* @return void
* @throws
* @date 2019/8/20 16:34
**/
@Async
public void doNoReturn(){
try {
// 這個方法執行需要三秒
Thread.sleep(3000);
System.out.println("方法執行結束" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* TODO 處理有返回的異步任務
* @param i
* @return java.util.concurrent.Future<java.lang.String>
* @throws
* @date 2019/8/20 16:35
**/
@Async
public Future<String> doReturn(int i){
try {
// 這個方法需要調用500毫秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 消息彙總
return new AsyncResult<>(String.format("這個是第{%s}個異步調用的證書", i));
}
}