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