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

- service2 執(zhí)行日志

- service3

總結(jié)
打完收工!
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Cloud引入Eureka組件,完善服務(wù)治理
這篇文章主要介紹了Spring Cloud引入Eureka組件,完善服務(wù)治理的過程詳解,幫助大家更好的理解和使用spring cloud,感興趣的朋友可以了解下2021-02-02
java實(shí)現(xiàn)獲取安卓設(shè)備里已安裝的軟件包
本文給大家介紹的是如何獲取設(shè)備中已經(jīng)安裝的應(yīng)用軟件包的代碼,其核心方法原理很簡(jiǎn)單,我們通過Android中提供的PackageManager類,來獲取手機(jī)中安裝的應(yīng)用程序信息2015-10-10
JAVA的Dubbo如何實(shí)現(xiàn)各種限流算法
Dubbo是一種高性能的Java RPC框架,廣泛應(yīng)用于分布式服務(wù)架構(gòu)中,在Dubbo中實(shí)現(xiàn)限流可以幫助服務(wù)在高并發(fā)場(chǎng)景下保持穩(wěn)定性和可靠性,常見的限流算法包括固定窗口算法、滑動(dòng)窗口算法、令牌桶算法和漏桶算法,在Dubbo中集成限流器可以通過實(shí)現(xiàn)自定義過濾器來實(shí)現(xiàn)2025-01-01
java中關(guān)于getProperties方法的使用
這篇文章主要介紹了java中關(guān)于getProperties方法的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Java 比較接口comparable與comparator區(qū)別解析
這篇文章主要介紹了Java 比較接口comparable與comparator區(qū)別解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
解決springboot+thymeleaf視圖映射報(bào)錯(cuò)There?was?an?unexpected?erro
這篇文章主要介紹了解決springboot+thymeleaf視圖映射報(bào)錯(cuò)There?was?an?unexpected?error?(type=Not?Found,?status=404)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12

