這是一個(gè)或許對(duì)你有用的開源項(xiàng)目
國(guó)產(chǎn) Star 破 10w+ 的開源項(xiàng)目,前端包括管理后臺(tái) + 微信小程序,后端支持單體和微服務(wù)架構(gòu)。
功能涵蓋 RBAC 權(quán)限、SaaS 多租戶、數(shù)據(jù)權(quán)限、商城、支付、工作流、大屏報(bào)表、微信公眾號(hào)等等功能:
來源:后端元宇宙
- 一個(gè)示例回顧Future
- 通過CompletableFuture實(shí)現(xiàn)上面示例
- CompletableFuture創(chuàng)建方式
- 異步回調(diào)方法
- 異常回調(diào)
- 多任務(wù)組合回調(diào)
- CompletableFuture使用有哪些注意點(diǎn)
一個(gè)示例回顧Future
一些業(yè)務(wù)場(chǎng)景我們需要使用多線程異步執(zhí)行任務(wù),加快任務(wù)執(zhí)行速度。
JDK5新增了Future
接口,用于描述一個(gè)異步計(jì)算的結(jié)果。
雖然 Future 以及相關(guān)使用方法提供了異步執(zhí)行任務(wù)的能力,但是對(duì)于結(jié)果的獲取卻是很不方便,我們必須使用Future.get()
的方式阻塞調(diào)用線程,或者使用輪詢方式判斷 Future.isDone
任務(wù)是否結(jié)束,再獲取結(jié)果。
這兩種處理方式都不是很優(yōu)雅,相關(guān)代碼如下:
@Test
publicvoidtestFuture()throwsExecutionException,InterruptedException{
ExecutorServiceexecutorService=Executors.newFixedThreadPool(5);
Futurefuture=executorService.submit(()->{
Thread.sleep(2000);
return"hello";
});
System.out.println(future.get());
System.out.println("end");
}
與此同時(shí),F(xiàn)uture無法解決多個(gè)異步任務(wù)需要相互依賴的場(chǎng)景,簡(jiǎn)單點(diǎn)說就是,主線程需要等待子線程任務(wù)執(zhí)行完畢之后在進(jìn)行執(zhí)行,這個(gè)時(shí)候你可能想到了CountDownLatch
,沒錯(cuò)確實(shí)可以解決,代碼如下。
這里定義兩個(gè)Future,第一個(gè)通過用戶id獲取用戶信息,第二個(gè)通過商品id獲取商品信息。
@Test
publicvoidtestCountDownLatch()throwsInterruptedException,ExecutionException{
ExecutorServiceexecutorService=Executors.newFixedThreadPool(5);
CountDownLatchdownLatch=newCountDownLatch(2);
longstartTime=System.currentTimeMillis();
FutureuserFuture=executorService.submit(()->{
//模擬查詢商品耗時(shí)500毫秒
Thread.sleep(500);
downLatch.countDown();
return"用戶A";
});
FuturegoodsFuture=executorService.submit(()->{
//模擬查詢商品耗時(shí)500毫秒
Thread.sleep(400);
downLatch.countDown();
return"商品A";
});
downLatch.await();
//模擬主程序耗時(shí)時(shí)間
Thread.sleep(600);
System.out.println("獲取用戶信息:"+userFuture.get());
System.out.println("獲取商品信息:"+goodsFuture.get());
System.out.println("總共用時(shí)"+(System.currentTimeMillis()-startTime)+"ms");
}
「運(yùn)行結(jié)果」
獲取用戶信息:用戶A
獲取商品信息:商品A
總共用時(shí)1110ms
從運(yùn)行結(jié)果可以看出結(jié)果都已經(jīng)獲取,而且如果我們不用異步操作,執(zhí)行時(shí)間應(yīng)該是:500+400+600 = 1500
,用異步操作后實(shí)際只用1110。
但是Java8以后我不在認(rèn)為這是一種優(yōu)雅的解決方式,接下來來了解下CompletableFuture
的使用。
基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
通過CompletableFuture實(shí)現(xiàn)上面示例
@Test
publicvoidtestCompletableInfo()throwsInterruptedException,ExecutionException{
longstartTime=System.currentTimeMillis();
//調(diào)用用戶服務(wù)獲取用戶基本信息
CompletableFutureuserFuture=CompletableFuture.supplyAsync(()->
//模擬查詢商品耗時(shí)500毫秒
{
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
e.printStackTrace();
}
return"用戶A";
});
//調(diào)用商品服務(wù)獲取商品基本信息
CompletableFuturegoodsFuture=CompletableFuture.supplyAsync(()->
//模擬查詢商品耗時(shí)500毫秒
{
try{
Thread.sleep(400);
}catch(InterruptedExceptione){
e.printStackTrace();
}
return"商品A";
});
System.out.println("獲取用戶信息:"+userFuture.get());
System.out.println("獲取商品信息:"+goodsFuture.get());
//模擬主程序耗時(shí)時(shí)間
Thread.sleep(600);
System.out.println("總共用時(shí)"+(System.currentTimeMillis()-startTime)+"ms");
}
運(yùn)行結(jié)果
獲取用戶信息:用戶A
獲取商品信息:商品A
總共用時(shí)1112ms
通過CompletableFuture
可以很輕松的實(shí)現(xiàn)CountDownLatch
的功能,你以為這就結(jié)束了,遠(yuǎn)遠(yuǎn)不止,CompletableFuture
比這要強(qiáng)多了。
比如可以實(shí)現(xiàn) :任務(wù)1執(zhí)行完了再執(zhí)行任務(wù)2,甚至任務(wù)1執(zhí)行的結(jié)果,作為任務(wù)2的入參數(shù)等等強(qiáng)大功能,下面就來學(xué)學(xué)CompletableFuture
的API。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
- 視頻教程:https://doc.iocoder.cn/video/
CompletableFuture創(chuàng)建方式
1、常用的4種創(chuàng)建方式
CompletableFuture
源碼中有四個(gè)靜態(tài)方法用來執(zhí)行異步任務(wù)
publicstaticCompletableFuturesupplyAsync(Suppliersupplier){..}
publicstaticCompletableFuturesupplyAsync(Suppliersupplier,Executorexecutor){..}
publicstaticCompletableFuturerunAsync(Runnablerunnable) {..}
publicstaticCompletableFuturerunAsync(Runnablerunnable,Executorexecutor) {..}
一般我們用上面的靜態(tài)方法來創(chuàng)建CompletableFuture
,這里也解釋下他們的區(qū)別:
- 「supplyAsync」 執(zhí)行任務(wù),支持返回值。
- 「runAsync」 執(zhí)行任務(wù),沒有返回值。
「supplyAsync方法」
//使用默認(rèn)內(nèi)置線程池ForkJoinPool.commonPool(),根據(jù)supplier構(gòu)建執(zhí)行任務(wù)
publicstaticCompletableFuturesupplyAsync(Suppliersupplier)
//自定義線程,根據(jù)supplier構(gòu)建執(zhí)行任務(wù)
publicstaticCompletableFuturesupplyAsync(Suppliersupplier,Executorexecutor)
「runAsync方法」
//使用默認(rèn)內(nèi)置線程池ForkJoinPool.commonPool(),根據(jù)runnable構(gòu)建執(zhí)行任務(wù)
publicstaticCompletableFuturerunAsync(Runnablerunnable)
//自定義線程,根據(jù)runnable構(gòu)建執(zhí)行任務(wù)
publicstaticCompletableFuturerunAsync(Runnablerunnable,Executorexecutor)
2、結(jié)果獲取的4種方式
對(duì)于結(jié)果的獲取CompltableFuture
類提供了四種方式
//方式一
publicTget()
//方式二
publicTget(longtimeout,TimeUnitunit)
//方式三
publicTgetNow(TvalueIfAbsent)
//方式四
publicTjoin()
說明:
- 「get()和get(long timeout, TimeUnit unit)」 => 在Future中就已經(jīng)提供了,后者提供超時(shí)處理,如果在指定時(shí)間內(nèi)未獲取結(jié)果將拋出超時(shí)異常
- 「getNow」 => 立即獲取結(jié)果不阻塞,結(jié)果計(jì)算已完成將返回結(jié)果或計(jì)算過程中的異常,如果未計(jì)算完成將返回設(shè)定的valueIfAbsent值
- 「join」 => 方法里不會(huì)拋出異常
示例:
@Test
publicvoidtestCompletableGet()throwsInterruptedException,ExecutionException{
CompletableFuturecp1=CompletableFuture.supplyAsync(()->{
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
return"商品A";
});
//getNow方法測(cè)試
System.out.println(cp1.getNow("商品B"));
//join方法測(cè)試
CompletableFuturecp2=CompletableFuture.supplyAsync((()->1/0));
System.out.println(cp2.join());
System.out.println("-----------------------------------------------------");
//get方法測(cè)試
CompletableFuturecp3=CompletableFuture.supplyAsync((()->1/0));
System.out.println(cp3.get());
}
「運(yùn)行結(jié)果」 :
- 第一個(gè)執(zhí)行結(jié)果為 「商品B」 ,因?yàn)橐人?秒結(jié)果不能立即獲取
-
join方法獲取結(jié)果方法里不會(huì)拋異常,但是執(zhí)行結(jié)果會(huì)拋異常,拋出的異常為
CompletionException
-
get方法獲取結(jié)果方法里將拋出異常,執(zhí)行結(jié)果拋出的異常為
ExecutionException
異步回調(diào)方法
1、thenRun/thenRunAsync
通俗點(diǎn)講就是,「做完第一個(gè)任務(wù)后,再做第二個(gè)任務(wù),第二個(gè)任務(wù)也沒有返回值」 。
示例
@Test
publicvoidtestCompletableThenRunAsync()throwsInterruptedException,ExecutionException{
longstartTime=System.currentTimeMillis();
CompletableFuturecp1=CompletableFuture.runAsync(()->{
try{
//執(zhí)行任務(wù)A
Thread.sleep(600);
}catch(InterruptedExceptione){
e.printStackTrace();
}
});
CompletableFuturecp2=cp1.thenRun(()->{
try{
//執(zhí)行任務(wù)B
Thread.sleep(400);
}catch(InterruptedExceptione){
e.printStackTrace();
}
});
//get方法測(cè)試
System.out.println(cp2.get());
//模擬主程序耗時(shí)時(shí)間
Thread.sleep(600);
System.out.println("總共用時(shí)"+(System.currentTimeMillis()-startTime)+"ms");
}
//運(yùn)行結(jié)果
/**
*null
*總共用時(shí)1610ms
*/
「thenRun 和thenRunAsync有什么區(qū)別呢?」
如果你執(zhí)行第一個(gè)任務(wù)的時(shí)候,傳入了一個(gè)自定義線程池:
- 調(diào)用thenRun方法執(zhí)行第二個(gè)任務(wù)時(shí),則第二個(gè)任務(wù)和第一個(gè)任務(wù)是共用同一個(gè)線程池。
-
調(diào)用
thenRunAsync
執(zhí)行第二個(gè)任務(wù)時(shí),則第一個(gè)任務(wù)使用的是你自己傳入的線程池,第二個(gè)任務(wù)使用的是ForkJoin線程池。
說明: 后面介紹的thenAccept
和thenAcceptAsync
,thenApply
和thenApplyAsync
等,它們之間的區(qū)別也是這個(gè)。
2、thenAccept/thenAcceptAsync
第一個(gè)任務(wù)執(zhí)行完成后,執(zhí)行第二個(gè)回調(diào)方法任務(wù),會(huì)將該任務(wù)的執(zhí)行結(jié)果,作為入?yún)?/strong> ,傳遞到回調(diào)方法中,但是回調(diào)方法是沒有返回值的。
示例
@Test
publicvoidtestCompletableThenAccept()throwsExecutionException,InterruptedException{
longstartTime=System.currentTimeMillis();
CompletableFuturecp1=CompletableFuture.supplyAsync(()->{
return"dev";
});
CompletableFuturecp2=cp1.thenAccept((a)->{
System.out.println("上一個(gè)任務(wù)的返回結(jié)果為:"+a);
});
cp2.get();
}
3、 thenApply/thenApplyAsync
表示第一個(gè)任務(wù)執(zhí)行完成后,執(zhí)行第二個(gè)回調(diào)方法任務(wù),會(huì)將該任務(wù)的執(zhí)行結(jié)果,作為入?yún)ⅲ瑐鬟f到回調(diào)方法中,并且回調(diào)方法是有返回值的。
示例
@Test
publicvoidtestCompletableThenApply()throwsExecutionException,InterruptedException{
CompletableFuturecp1=CompletableFuture.supplyAsync(()->{
return"dev";
}).thenApply((a)->{
if(Objects.equals(a,"dev")){
return"dev";
}
return"prod";
});
System.out.println("當(dāng)前環(huán)境為:"+cp1.get());
//輸出:當(dāng)前環(huán)境為:dev
}
異常回調(diào)
當(dāng)CompletableFuture
的任務(wù)不論是正常完成還是出現(xiàn)異常它都會(huì)調(diào)用 「whenComplete」 這回調(diào)函數(shù)。
- 「正常完成」 :whenComplete返回結(jié)果和上級(jí)任務(wù)一致,異常為null;
- 「出現(xiàn)異常」 :whenComplete返回結(jié)果為null,異常為上級(jí)任務(wù)的異常;
即調(diào)用get()
時(shí),正常完成時(shí)就獲取到結(jié)果,出現(xiàn)異常時(shí)就會(huì)拋出異常,需要你處理該異常。
下面來看看示例
1、只用whenComplete
@Test
publicvoidtestCompletableWhenComplete()throwsExecutionException,InterruptedException{
CompletableFuturefuture=CompletableFuture.supplyAsync(()->{
if(Math.random()0.5){
thrownewRuntimeException("出錯(cuò)了");
}
System.out.println("正常結(jié)束");
return0.11;
}).whenComplete((aDouble,throwable)->{
if(aDouble==null){
System.out.println("whenCompleteaDoubleisnull");
}else{
System.out.println("whenCompleteaDoubleis"+aDouble);
}
if(throwable==null){
System.out.println("whenCompletethrowableisnull");
}else{
System.out.println("whenCompletethrowableis"+throwable.getMessage());
}
});
System.out.println("最終返回的結(jié)果="+future.get());
}
正常完成,沒有異常時(shí):
正常結(jié)束
whenCompleteaDoubleis0.11
whenCompletethrowableisnull
最終返回的結(jié)果=0.11
出現(xiàn)異常時(shí):get()會(huì)拋出異常
whenCompleteaDoubleisnull
whenCompletethrowableisjava.lang.RuntimeException:出錯(cuò)了
java.util.concurrent.ExecutionException:java.lang.RuntimeException:出錯(cuò)了
atjava.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
atjava.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
2、whenComplete + exceptionally示例
@Test
publicvoidtestWhenCompleteExceptionally()throwsExecutionException,InterruptedException{
CompletableFuturefuture=CompletableFuture.supplyAsync(()->{
if(Math.random()0.5){
thrownewRuntimeException("出錯(cuò)了");
}
System.out.println("正常結(jié)束");
return0.11;
}).whenComplete((aDouble,throwable)->{
if(aDouble==null){
System.out.println("whenCompleteaDoubleisnull");
}else{
System.out.println("whenCompleteaDoubleis"+aDouble);
}
if(throwable==null){
System.out.println("whenCompletethrowableisnull");
}else{
System.out.println("whenCompletethrowableis"+throwable.getMessage());
}
}).exceptionally((throwable)->{
System.out.println("exceptionally中異常:"+throwable.getMessage());
return0.0;
});
System.out.println("最終返回的結(jié)果="+future.get());
}
當(dāng)出現(xiàn)異常時(shí),exceptionally
中會(huì)捕獲該異常,給出默認(rèn)返回值0.0。
whenCompleteaDoubleisnull
whenCompletethrowableisjava.lang.RuntimeException:出錯(cuò)了
exceptionally中異常:java.lang.RuntimeException:出錯(cuò)了
最終返回的結(jié)果=0.0
多任務(wù)組合回調(diào)
1、AND組合關(guān)系
thenCombine
/ thenAcceptBoth
/ runAfterBoth
都表示:「當(dāng)任務(wù)一和任務(wù)二都完成再執(zhí)行任務(wù)三」 。
區(qū)別在于:
- 「runAfterBoth」 不會(huì)把執(zhí)行結(jié)果當(dāng)做方法入?yún)ⅲ覜]有返回值
- 「thenAcceptBoth」 : 會(huì)將兩個(gè)任務(wù)的執(zhí)行結(jié)果作為方法入?yún)ⅲ瑐鬟f到指定方法中,且無返回值
- 「thenCombine」 :會(huì)將兩個(gè)任務(wù)的執(zhí)行結(jié)果作為方法入?yún)ⅲ瑐鬟f到指定方法中,且有返回值
示例
@Test
publicvoidtestCompletableThenCombine()throwsExecutionException,InterruptedException{
//創(chuàng)建線程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(10);
//開啟異步任務(wù)1
CompletableFuturetask=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)1,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+1;
System.out.println("異步任務(wù)1結(jié)束");
returnresult;
},executorService);
//開啟異步任務(wù)2
CompletableFuturetask2=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)2,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+1;
System.out.println("異步任務(wù)2結(jié)束");
returnresult;
},executorService);
//任務(wù)組合
CompletableFuturetask3=task.thenCombineAsync(task2,(f1,f2)->{
System.out.println("執(zhí)行任務(wù)3,當(dāng)前線程是:"+Thread.currentThread().getId());
System.out.println("任務(wù)1返回值:"+f1);
System.out.println("任務(wù)2返回值:"+f2);
returnf1+f2;
},executorService);
Integerres=task3.get();
System.out.println("最終結(jié)果:"+res);
}
「運(yùn)行結(jié)果」
異步任務(wù)1,當(dāng)前線程是:17
異步任務(wù)1結(jié)束
異步任務(wù)2,當(dāng)前線程是:18
異步任務(wù)2結(jié)束
執(zhí)行任務(wù)3,當(dāng)前線程是:19
任務(wù)1返回值:2
任務(wù)2返回值:2
最終結(jié)果:4
2、OR組合關(guān)系
applyToEither
/ acceptEither
/ runAfterEither
都表示:「兩個(gè)任務(wù),只要有一個(gè)任務(wù)完成,就執(zhí)行任務(wù)三」 。
區(qū)別在于:
- 「runAfterEither」 :不會(huì)把執(zhí)行結(jié)果當(dāng)做方法入?yún)ⅲ覜]有返回值
- 「acceptEither」 : 會(huì)將已經(jīng)執(zhí)行完成的任務(wù),作為方法入?yún)ⅲ瑐鬟f到指定方法中,且無返回值
- 「applyToEither」 :會(huì)將已經(jīng)執(zhí)行完成的任務(wù),作為方法入?yún)ⅲ瑐鬟f到指定方法中,且有返回值
示例
@Test
publicvoidtestCompletableEitherAsync(){
//創(chuàng)建線程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(10);
//開啟異步任務(wù)1
CompletableFuturetask=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)1,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+1;
System.out.println("異步任務(wù)1結(jié)束");
returnresult;
},executorService);
//開啟異步任務(wù)2
CompletableFuturetask2=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)2,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+2;
try{
Thread.sleep(3000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("異步任務(wù)2結(jié)束");
returnresult;
},executorService);
//任務(wù)組合
task.acceptEitherAsync(task2,(res)->{
System.out.println("執(zhí)行任務(wù)3,當(dāng)前線程是:"+Thread.currentThread().getId());
System.out.println("上一個(gè)任務(wù)的結(jié)果為:"+res);
},executorService);
}
運(yùn)行結(jié)果
//通過結(jié)果可以看出,異步任務(wù)2都沒有執(zhí)行結(jié)束,任務(wù)3獲取的也是1的執(zhí)行結(jié)果
異步任務(wù)1,當(dāng)前線程是:17
異步任務(wù)1結(jié)束
異步任務(wù)2,當(dāng)前線程是:18
執(zhí)行任務(wù)3,當(dāng)前線程是:19
上一個(gè)任務(wù)的結(jié)果為:2
注意
如果把上面的核心線程數(shù)改為1也就是
ExecutorServiceexecutorService=Executors.newFixedThreadPool(1);
運(yùn)行結(jié)果就是下面的了,會(huì)發(fā)現(xiàn)根本沒有執(zhí)行任務(wù)3,顯然是任務(wù)3直接被丟棄了。
異步任務(wù)1,當(dāng)前線程是:17
異步任務(wù)1結(jié)束
異步任務(wù)2,當(dāng)前線程是:17
3、多任務(wù)組合
- 「allOf」 :等待所有任務(wù)完成
- 「anyOf」 :只要有一個(gè)任務(wù)完成
示例
allOf:等待所有任務(wù)完成
@Test
publicvoidtestCompletableAallOf()throwsExecutionException,InterruptedException{
//創(chuàng)建線程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(10);
//開啟異步任務(wù)1
CompletableFuturetask=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)1,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+1;
System.out.println("異步任務(wù)1結(jié)束");
returnresult;
},executorService);
//開啟異步任務(wù)2
CompletableFuturetask2=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)2,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+2;
try{
Thread.sleep(3000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("異步任務(wù)2結(jié)束");
returnresult;
},executorService);
//開啟異步任務(wù)3
CompletableFuturetask3=CompletableFuture.supplyAsync(()->{
System.out.println("異步任務(wù)3,當(dāng)前線程是:"+Thread.currentThread().getId());
intresult=1+3;
try{
Thread.sleep(4000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("異步任務(wù)3結(jié)束");
returnresult;
},executorService);
//任務(wù)組合
CompletableFutureallOf=CompletableFuture.allOf(task,task2,task3);
//等待所有任務(wù)完成
allOf.get();
//獲取任務(wù)的返回結(jié)果
System.out.println("task結(jié)果為:"+task.get());
System.out.println("task2結(jié)果為:"+task2.get());
System.out.println("task3結(jié)果為:"+task3.get());
}
anyOf: 只要有一個(gè)任務(wù)完成
@Test
publicvoidtestCompletableAnyOf()throwsExecutionException,InterruptedException{
//創(chuàng)建線程池
ExecutorServiceexecutorService=Executors.newFixedThreadPool(10);
//開啟異步任務(wù)1
CompletableFuturetask=CompletableFuture.supplyAsync(()->{
intresult=1+1;
returnresult;
},executorService);
//開啟異步任務(wù)2
CompletableFuturetask2=CompletableFuture.supplyAsync(()->{
intresult=1+2;
returnresult;
},executorService);
//開啟異步任務(wù)3
CompletableFuturetask3=CompletableFuture.supplyAsync(()->{
intresult=1+3;
returnresult;
},executorService);
//任務(wù)組合
CompletableFuture
CompletableFuture使用有哪些注意點(diǎn)
CompletableFuture
使我們的異步編程更加便利的、代碼更加優(yōu)雅的同時(shí),我們也要關(guān)注下它,使用的一些注意點(diǎn)。
1、Future需要獲取返回值,才能獲取異常信息
@Test
publicvoidtestWhenCompleteExceptionally(){
CompletableFuturefuture=CompletableFuture.supplyAsync(()->{
if(1==1){
thrownewRuntimeException("出錯(cuò)了");
}
return0.11;
});
//如果不加get()方法這一行,看不到異常信息
//future.get();
}
Future需要獲取返回值,才能獲取到異常信息。如果不加 get()
/join()
方法,看不到異常信息。
小伙伴們使用的時(shí)候,注意一下哈,考慮是否加try...catch...
或者使用exceptionally
方法。
2、CompletableFuture的get()方法是阻塞的
CompletableFuture
的get()
方法是阻塞的,如果使用它來獲取異步調(diào)用的返回值,需要添加超時(shí)時(shí)間。
//反例
CompletableFuture.get();
//正例
CompletableFuture.get(5,TimeUnit.SECONDS);
3、不建議使用默認(rèn)線程池
CompletableFuture
代碼中又使用了默認(rèn)的 「ForkJoin線程池」 ,處理的線程個(gè)數(shù)是電腦 「CPU核數(shù)-1」 。在大量請(qǐng)求過來的時(shí)候,處理邏輯復(fù)雜的話,響應(yīng)會(huì)很慢。一般建議使用自定義線程池,優(yōu)化線程池配置參數(shù)。
4、自定義線程池時(shí),注意飽和策略
CompletableFuture
的get()方法是阻塞的,我們一般建議使用future.get(5, TimeUnit.SECONDS)
。并且一般建議使用自定義線程池。
但是如果線程池拒絕策略是DiscardPolicy
或者DiscardOldestPolicy
,當(dāng)線程池飽和時(shí),會(huì)直接丟棄任務(wù),不會(huì)拋棄異常。因此建議,CompletableFuture
線程池策略最好使用AbortPolicy
,然后耗時(shí)的異步線程,做好線程池隔離哈。
-
代碼
+關(guān)注
關(guān)注
30文章
4748瀏覽量
68351 -
異步
+關(guān)注
關(guān)注
0文章
62瀏覽量
18034 -
線程
+關(guān)注
關(guān)注
0文章
504瀏覽量
19651
原文標(biāo)題:奇淫巧技,CompletableFuture 異步多線程是真的優(yōu)雅
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論