你能列舉CompletableFuture的幾種用法嗎揽碘?
對CompletableFuture類,是不是既熟悉又陌生?
熟悉的是項目里經(jīng)常用到钾菊,陌生的是那些都是別人寫的帅矗,直到某個項目打包啟動后,既不運(yùn)行也沒有異常爆出煞烫?浑此!尷尬了,趕緊研究下吧滞详!雖然最開始并沒有懷疑是它的問題凛俱,可是最后問題就定位在這里。
CompletableFuture是java.util.concurrent并發(fā)線程工具包中在java8新增特性料饥,從字面意思看是可完成的Future蒲犬,可以主動設(shè)置返回值和狀態(tài),其支持流式計算岸啡、函數(shù)式編程原叮、完成通知、自定義異常處理等新特性⊙舱海現(xiàn)在看看最常規(guī)的場景及使用奋隶。
第一,創(chuàng)建一個異步任務(wù)悦荒,獲取異步任務(wù)的執(zhí)行結(jié)果唯欣;
// ForkJoin的線程池來執(zhí)行被提交的任務(wù)
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
System.out.println("compute test");
return "test";
});
// 通過get或join獲取最終返回結(jié)果
String result = future.join();
System.out.println("get result: " + result);
執(zhí)行結(jié)果:
compute test
get result: test
supplyAsync有兩個參數(shù),執(zhí)行的任務(wù)和線程池搬味,這里使用默認(rèn)的線程池境氢,默認(rèn)的線程池是ForkJoinPool類。
默認(rèn)的線程池:
第二碰纬,先執(zhí)行第一個任務(wù)再執(zhí)行第二個任務(wù) ****thenApply****萍聊;
// 先執(zhí)行第一個任務(wù)
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
// 執(zhí)行完畢后,返回一個值
return 1;
});
// 傳入第一個任務(wù)的執(zhí)行結(jié)果(第一個任務(wù)必須執(zhí)行完畢)悦析,執(zhí)行第二個任務(wù)
// thenApply(有返回值脐区,有入?yún)? / thenAccept(無返回值,有入?yún)? / thenRun(無返回值她按,無入?yún)?
CompletableFuture<Integer> future2 = future1.thenApply((p)->{
System.out.println("compute 2");
// 執(zhí)行完畢后牛隅,返回兩個執(zhí)行結(jié)果之和
return p+10;
});
System.out.println("result: " + future2.join());
運(yùn)行結(jié)果:
compute 1
compute 2
result: 11
說明:兩個任務(wù)中有依賴關(guān)系。
thenApply能接收上一個任務(wù)的返回值(有入?yún)ⅲ┳锰⑶铱梢詒eturn媒佣;
除了thenApply,還有thenAccept和thenRun陵刹。
三種方法的區(qū)別:thenApply(有返回值默伍,有入?yún)? ;
thenAccept(無返回值,有入?yún)? 也糊,
thenRun(無返回值炼蹦,無入?yún)?。
thenCompose把兩個任務(wù)組合:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
// 任務(wù)1組合任務(wù)2
CompletableFuture<Integer> future2 = future1.thenCompose((r)->CompletableFuture.supplyAsync(()->r+10));
System.out.println(future2.join());
至于thenApply和thenCompose有什么區(qū)別狸剃,需要在實戰(zhàn)中才能摸索出來掐隐,從一個簡易的demo是看不出來什么的。
第三钞馁,****thenCombine****合并兩個任務(wù)的執(zhí)行結(jié)果虑省;
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
// 異步執(zhí)行任務(wù)2
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 2");
return 10;
});
// 將執(zhí)行結(jié)果合并
CompletableFuture<Integer> future3 = future1.thenCombine(future2, (r1, r2)->r1 + r2);
System.out.println("result: " + future3.join());
運(yùn)行結(jié)果:
compute 1
compute 2
result: 11
說明:兩個任務(wù)中沒有依賴關(guān)系,但需要等待兩個任務(wù)都執(zhí)行完畢僧凰。
第四探颈,任務(wù)完成時的回調(diào)通知****,whenComplete或handle****训措;
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
CompletableFuture future2 = future1.whenComplete((r, e)->{
if(e != null){
System.out.println("compute failed!");
} else {
System.out.println("received result is " + r);
}
});
System.out.println("result: " + future2.join());
運(yùn)行結(jié)果:
compute 1
received result is 1
result: 1
想要future2也有返回值伪节,那就使用handler吧;
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
CompletableFuture<Integer> future2 = future1.handle((r, e)->{
if(e != null){
System.out.println("compute failed!");
return r;
} else {
System.out.println("received result is " + r);
return r + 10;
}
});
System.out.println("result: " + future2.join());
handler可以return绩鸣,whenComplete無返回值;
第五****怀大,****兩個任務(wù)完成一個就返回,applyToEitherAsync 或 anyOf全闷;
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("compute 1");
return 1;
});
CompletableFuture<Integer> future2 = future1.handle((r, e)->{
if(e != null){
System.out.println("compute failed!");
return r;
} else {
System.out.println("received result is " + r);
return r + 10;
}
});
System.out.println("result: " + future2.join());
運(yùn)行結(jié)果:
任務(wù)1
任務(wù)3,接收的結(jié)果:1
最終結(jié)果:1
說明:這里可以把兩個任務(wù)的執(zhí)行時間進(jìn)行調(diào)整萍启,這樣就可以得到不同的運(yùn)行結(jié)果总珠。
如果任務(wù)不止兩個,試試anyOf吧勘纯。
// anyOf局服,多個任務(wù)中,完成一個就返回
CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("任務(wù)1");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "1";
});
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("任務(wù)2");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "2";
});
CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
System.out.println("任務(wù)3");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "3";
});
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future01, future02, future03);
//獲取異步任務(wù)的返回值
try {
System.out.println("返回結(jié)果:" + anyOf.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
第六驳遵,allof 等待所有任務(wù)都執(zhí)行完淫奔;
// allof等待所有任務(wù)完成
CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("任務(wù)1");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "1";
});
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("任務(wù)2");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "2";
});
CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
System.out.println("任務(wù)3");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "3";
});
CompletableFuture<Void> allOf = CompletableFuture.allOf(future01, future02, future03);
//獲取異步任務(wù)的返回值
try {
allOf.get();//等待所有結(jié)果完成
System.out.println("返回結(jié)果:" + future01.get() + future02.get() + future03.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
運(yùn)行結(jié)果:
任務(wù)2
任務(wù)3
任務(wù)1
返回結(jié)果:123
第七****,exceptionally 捕獲****執(zhí)行****中的錯誤堤结;
// 7. 在使用supplyAsync異步調(diào)用時唆迁,出現(xiàn)異常打印日志中看不到,可以通過exceptionally處理
CompletableFuture.supplyAsync(()->{
try{
String[] aa = {"aa","bb"};
System.out.println(aa[2]);
}catch(Exception e){
throw new RuntimeException("出現(xiàn)異常了");
}
return 1;
}).whenComplete((r, e)->{
if(e != null){
//第一個任務(wù)失敗了
} else {
//第一個任務(wù)成功后做什么
}
}).exceptionally(e->{
CompletionException ex = new CompletionException(e);
System.out.println(ex);
throw ex;
});
運(yùn)行結(jié)果:
java.util.concurrent.CompletionException: java.util.concurrent.CompletionException: java.lang.RuntimeException: 出現(xiàn)異常了
任務(wù)執(zhí)行過程中竞穷,出現(xiàn)錯誤唐责,無法打印在日志里,將是一件很糟糕的事情瘾带。
最后總結(jié)
1.異步任務(wù)的建立supplyAsync鼠哥,獲得任務(wù)的執(zhí)行結(jié)果get()或join();
2.thenApply、thenAccept朴恳、thenRun傳入?yún)?shù)和返回值的差異抄罕,可以根據(jù)需要使用;
3.thenApply于颖、thenCompose呆贿、thenCombine,根據(jù)任務(wù)間的關(guān)系使用對應(yīng)關(guān)系的方法恍飘;
4.exceptionally及時捕捉異常的業(yè)務(wù)邏輯榨崩,以免找不到錯誤的根源;
5.如同2章母,很多方法方法名相似母蛛,用法上略有差異,這個還需要在實戰(zhàn)中摸索乳怎。
參考文檔: