Springboot?@Async多線程獲取返回值方式
@Async 多線程獲取返回值
最近需要用到多線程 自己維護線程池很麻煩 正好看到 springboot 集成線程池的例子 這里自己做了個嘗試和總結 記錄一下 也分享給需要的朋友;
不考慮事務的情況下 這個多線程實現(xiàn)比較簡單
主要有以下幾點
- 在啟動類加上 @EnableAsync 注解, 開啟異步執(zhí)行支持;
- 編寫線程池配置類, 別忘了 @Configuration , 和 @Bean 注解;
- 編寫需要異步執(zhí)行的業(yè)務, 放到單獨的類中 (可以定義為 service, 因為需要 spring 管理起來才能用 );
- 在業(yè)務service中調用異步執(zhí)行的service, 注意這是重點, 不能直接在業(yè)務 service 中寫異步執(zhí)行的代碼, 否則無法異步執(zhí)行( 這就是單獨放異步代碼的原因);
好了, 上代碼:
// 啟動類
@EnableAsync
@EnableWebSecurity
@ServletComponentScan
@SpringBootApplication(scanBasePackages={"com.example.demo"})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}// 線程池配置類
@Slf4j
@Configuration
public class BootThreadpoolConfig {
// 配置核心線程數(shù)
private int corePoolSize = 5;
// 配置最大線程數(shù)
private int maxPoolSize = 20;
// 配置任務隊列的長度
private int queueCapacity = 500;
// 配置任務的空閑時間
private int aliveSeconds = 600;
// 配置線程前綴
private String namePrefix = "localThreadPool";
// 自定義線程池, 起個好記的名
@Bean(name = "localBootAsyncExecutor")
public Executor asyncServiceExecutor() {
log.info("初始化 springboot 線程池");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心線程數(shù)
executor.setCorePoolSize(corePoolSize);
//配置最大線程數(shù)
executor.setMaxPoolSize(maxPoolSize);
//配置隊列大小
executor.setQueueCapacity(queueCapacity);
//配置線程池中的線程的名稱前綴
executor.setThreadNamePrefix(namePrefix);
//配置線程的空閑時間
executor.setKeepAliveSeconds(aliveSeconds);
// RejectedExecutionHandler:當pool已經(jīng)達到max size的時候,如何處理新任務
// CallerRunsPolicy:不在新線程中執(zhí)行任務,而是有調用者所在的線程來執(zhí)行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//執(zhí)行初始化
executor.initialize();
log.info("springboot 線程池初始化完畢");
return executor;
}
}// 異步執(zhí)行代碼
@Service("asyncExecutorTest")
public class AsyncExecutorTest {
// 異步執(zhí)行的方法, 注解內為自定義線程池類名
@Async("localBootAsyncExecutor")
public Future<Integer> test1(Integer i) throws InterruptedException {
Thread.sleep(100);
System.out.println("@Async 執(zhí)行: " + i);
return new AsyncResult(i);
}
// 這里使用其它方式調用,詳見后面的 service3 方法
public Integer test2(Integer i) throws InterruptedException {
Thread.sleep(100);
System.out.println(" excute.run 執(zhí)行: " + i);
return i;
}
}// 業(yè)務 service
@Service("asyncExcutorService")
public class AsyncExcutorService {
@Autowired
AsyncExecutorTest asyncExecutorTest;
@Autowired
Executor localBootAsyncExecutor;
// 測試 無返回值異步執(zhí)行
public void service1(){
System.out.println("service1 執(zhí)行----->");
for (int i = 0; i < 50; i++) {
try {
asyncExecutorTest.test1(i);
} catch (InterruptedException e) {
System.out.println("service1執(zhí)行出錯");
}
}
System.out.println("service1 結束----->");
}
// 測試 有返回值異步執(zhí)行
public void service2(){
long l = System.currentTimeMillis();
System.out.println("service2 執(zhí)行----->");
List<Future> result = new ArrayList<>();
try {
for (int i = 0; i < 300; i++) {
Future<Integer> integerFuture = asyncExecutorTest.test1(i);
result.add(integerFuture);
}
for (Future future : result) {
System.out.println(future.get());
}
} catch (InterruptedException | ExecutionException e) {
System.out.println("service2執(zhí)行出錯");
}
System.out.println("service2 結束----->" + (System.currentTimeMillis() - l));
}
// 測試 有返回值異步執(zhí)行
public void service3(){
long l = System.currentTimeMillis();
List<Integer> result = new ArrayList<>();
try {
System.out.println("service3 執(zhí)行----->");
int total = 300;
CountDownLatch latch = new CountDownLatch(total);
for (int i = 0; i < total; i++) {
final int y = i;
localBootAsyncExecutor.execute(() -> {
try {
result.add(asyncExecutorTest.test2(y));
} catch (InterruptedException e) {
System.out.println("service3執(zhí)行出錯");
} finally {
latch.countDown();
}
});
}
latch.await();
} catch (InterruptedException e) {
System.out.println("service3執(zhí)行出錯");
}
System.out.println("service3 結束----->" + (System.currentTimeMillis() - l));
}
}這里說下service1 和 service2的區(qū)別
1. 兩個都用的是一個線程池執(zhí)行的
2. service1 單純執(zhí)行業(yè)務, 不用返回數(shù)據(jù), 主線程也不用等待
3. service2 需要返回數(shù)據(jù), 主線程需要等待結果( 注意返回值只能是 Future, 最后再 .get()去獲取, 否則無法異步執(zhí)行)
4. service3 也可以返回數(shù)據(jù), 但是書寫上麻煩一些. 返回值直接是想要的結果, 不像 service2 還需要提取一次數(shù)據(jù).
其它就是 controller 了, 自己隨便寫一個調用一下就行了, 這里就不寫了, 以下是我測試的 console 日志
- service1 執(zhí)行日志

- service2 執(zhí)行日志

- service3

總結
打完收工!
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java 比較接口comparable與comparator區(qū)別解析
這篇文章主要介紹了Java 比較接口comparable與comparator區(qū)別解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-10-10
解決springboot+thymeleaf視圖映射報錯There?was?an?unexpected?erro
這篇文章主要介紹了解決springboot+thymeleaf視圖映射報錯There?was?an?unexpected?error?(type=Not?Found,?status=404)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12

