目錄
1、thenCombine / thenAcceptBoth / runAfterBoth
2、applyToEither / acceptEither / runAfterEither
CompletableFuture實現了CompletionStage接口和Future接口,前者是對後者的一個擴展,增加了異步回調、流式處理、多個Future組合處理的能力,使Java在處理多任務的協同工作時更加順暢便利。
一、創建異步任務
1、Future.submit
通常的線程池接口類ExecutorService,其中execute方法的返回值是void,即無法獲取異步任務的執行狀態,3個重載的submit方法的返回值是Future,可以據此獲取任務執行的狀態和結果,示例如下:
@Test
public void test3() throws Exception {
// 創建異步執行任務:
ExecutorService executorService= Executors.newSingleThreadExecutor();
Future<Double> cf = executorService.submit(()->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(false){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
return 1.2;
}
});
System.out.println("main thread start,time->"+System.currentTimeMillis());
//等待子任務執行完成,如果已完成則直接返回結果
//如果執行任務異常,則get方法會把之前捕獲的異常重新拋出
System.out.println("run result->"+cf.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
執行結果如下:
子線程是異步執行的,主線程休眠等待子線程執行完成,子線程執行完成後喚醒主線程,主線程獲取任務執行結果後退出。
很多博客說使用不帶等待時間限制的get方法時,如果子線程執行異常了會導致主線程長期阻塞,這其實是錯誤的,子線程執行異常時其異常會被捕獲,然後修改任務的狀態爲異常結束並喚醒等待的主線程,get方法判斷任務狀態發生變更,就終止等待了,並拋出異常,可參考《Java8 AbstractExecutorService 和 FutureTask 源碼解析》中FutureTask的實現。將上述用例中if(false)改成if(true) ,執行結果如下:
get方法拋出異常導致主線程異常終止。
2、supplyAsync / runAsync
supplyAsync表示創建帶返回值的異步任務的,相當於ExecutorService submit(Callable<T> task) 方法,runAsync表示創建無返回值的異步任務,相當於ExecutorService submit(Runnable task)方法,這兩方法的效果跟submit是一樣的,測試用例如下:
@Test
public void test2() throws Exception {
// 創建異步執行任務,有返回值
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(true){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
return 1.2;
}
});
System.out.println("main thread start,time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("run result->"+cf.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
@Test
public void test4() throws Exception {
// 創建異步執行任務,無返回值
CompletableFuture cf = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(false){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
}
});
System.out.println("main thread start,time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("run result->"+cf.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
這兩方法各有一個重載版本,可以指定執行異步任務的Executor實現,如果不指定,默認使用ForkJoinPool.commonPool(),如果機器是單核的,則默認使用ThreadPerTaskExecutor,該類是一個內部類,每次執行execute都會創建一個新線程。測試用例如下:
@Test
public void test2() throws Exception {
ForkJoinPool pool=new ForkJoinPool();
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(true){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
return 1.2;
}
},pool);
System.out.println("main thread start,time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("run result->"+cf.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
@Test
public void test4() throws Exception {
ExecutorService executorService= Executors.newSingleThreadExecutor();
// 創建異步執行任務:
CompletableFuture cf = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(false){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
}
},executorService);
System.out.println("main thread start,time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("run result->"+cf.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
二、異步回調
1、thenApply / thenApplyAsync
thenApply 表示某個任務執行完成後執行的動作,即回調方法,會將該任務的執行結果即方法返回值作爲入參傳遞到回調方法中,測試用例如下:
@Test
public void test5() throws Exception {
ForkJoinPool pool=new ForkJoinPool();
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
},pool);
//cf關聯的異步任務的返回值作爲方法入參,傳入到thenApply的方法中
//thenApply這裏實際創建了一個新的CompletableFuture實例
CompletableFuture<String> cf2=cf.thenApply((result)->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return "test:"+result;
});
System.out.println("main thread start cf.get(),time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("run result->"+cf.get());
System.out.println("main thread start cf2.get(),time->"+System.currentTimeMillis());
System.out.println("run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其執行結果如下:
job1執行結束後,將job1的方法返回值作爲入參傳遞到job2中並立即執行job2。thenApplyAsync與thenApply的區別在於,前者是將job2提交到線程池中異步執行,實際執行job2的線程可能是另外一個線程,後者是由執行job1的線程立即執行job2,即兩個job都是同一個線程執行的。將上述測試用例中thenApply改成thenApplyAsync後,執行結果如下:
從輸出可知,執行job1和job2是兩個不同的線程。thenApplyAsync有一個重載版本,可以指定執行異步任務的Executor實現,如果不指定,默認使用ForkJoinPool.commonPool()。 下述的多個方法,每個方法都有兩個以Async結尾的方法,一個使用默認的Executor實現,一個使用指定的Executor實現,不帶Async的方法是由觸發該任務的線程執行該任務,帶Async的方法是由觸發該任務的線程將任務提交到線程池,執行任務的線程跟觸發任務的線程不一定是同一個。
2、thenAccept / thenRun
thenAccept 同 thenApply 接收上一個任務的返回值作爲參數,但是無返回值;thenRun 的方法沒有入參,也買有返回值,測試用例如下:
@Test
public void test6() throws Exception {
ForkJoinPool pool=new ForkJoinPool();
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
},pool);
//cf關聯的異步任務的返回值作爲方法入參,傳入到thenApply的方法中
CompletableFuture cf2=cf.thenApply((result)->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return "test:"+result;
}).thenAccept((result)-> { //接收上一個任務的執行結果作爲入參,但是沒有返回值
System.out.println(Thread.currentThread()+" start job3,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(result);
System.out.println(Thread.currentThread()+" exit job3,time->"+System.currentTimeMillis());
}).thenRun(()->{ //無入參,也沒有返回值
System.out.println(Thread.currentThread()+" start job4,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("thenRun do something");
System.out.println(Thread.currentThread()+" exit job4,time->"+System.currentTimeMillis());
});
System.out.println("main thread start cf.get(),time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("run result->"+cf.get());
System.out.println("main thread start cf2.get(),time->"+System.currentTimeMillis());
//cf2 等待最後一個thenRun執行完成
System.out.println("run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其執行結果如下:
3、 exceptionally
exceptionally方法指定某個任務執行異常時執行的回調方法,會將拋出異常作爲參數傳遞到回調方法中,如果該任務正常執行則會exceptionally方法返回的CompletionStage的result就是該任務正常執行的結果,測試用例如下:
@Test
public void test2() throws Exception {
ForkJoinPool pool=new ForkJoinPool();
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+"job1 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(true){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+"job1 exit,time->"+System.currentTimeMillis());
return 1.2;
}
},pool);
//cf執行異常時,將拋出的異常作爲入參傳遞給回調方法
CompletableFuture<Double> cf2= cf.exceptionally((param)->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("error stack trace->");
param.printStackTrace();
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
return -1.1;
});
//cf正常執行時執行的邏輯,如果執行異常則不調用此邏輯
CompletableFuture cf3=cf.thenAccept((param)->{
System.out.println(Thread.currentThread()+"job2 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("param->"+param);
System.out.println(Thread.currentThread()+"job2 exit,time->"+System.currentTimeMillis());
});
System.out.println("main thread start,time->"+System.currentTimeMillis());
//等待子任務執行完成,此處無論是job2和job3都可以實現job2退出,主線程才退出,如果是cf,則主線程不會等待job2執行完成自動退出了
//cf2.get時,沒有異常,但是依然有返回值,就是cf的返回值
System.out.println("run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其輸出如下:
拋出異常後,只有cf2執行了,cf3沒有執行。將上述示例中的if(true) 改成if(false),其輸出如下:
cf2沒有指定,其result就是cf執行的結果,理論上cf2.get應該立即返回的,此處是等待了cf3,即job2執行完成後才返回,具體原因且待下篇源碼分析時再做探討。
4、whenComplete
whenComplete是當某個任務執行完成後執行的回調方法,會將執行結果或者執行期間拋出的異常傳遞給回調方法,如果是正常執行則異常爲null,回調方法對應的CompletableFuture的result和該任務一致,如果該任務正常執行,則get方法返回執行結果,如果是執行異常,則get方法拋出異常。測試用例如下:
@Test
public void test10() throws Exception {
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+"job1 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(false){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+"job1 exit,time->"+System.currentTimeMillis());
return 1.2;
}
});
//cf執行完成後會將執行結果和執行過程中拋出的異常傳入回調方法,如果是正常執行的則傳入的異常爲null
CompletableFuture<Double> cf2=cf.whenComplete((a,b)->{
System.out.println(Thread.currentThread()+"job2 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(b!=null){
System.out.println("error stack trace->");
b.printStackTrace();
}else{
System.out.println("run succ,result->"+a);
}
System.out.println(Thread.currentThread()+"job2 exit,time->"+System.currentTimeMillis());
});
//等待子任務執行完成
System.out.println("main thread start wait,time->"+System.currentTimeMillis());
//如果cf是正常執行的,cf2.get的結果就是cf執行的結果
//如果cf是執行異常,則cf2.get會拋出異常
System.out.println("run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
執行結果如下:
將上述示例中的if(false) 改成if(true),其輸出如下:
5、handle
跟whenComplete基本一致,區別在於handle必須指定方法的返回值,測試用例如下:
@Test
public void test10() throws Exception {
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+"job1 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(true){
throw new RuntimeException("test");
}else{
System.out.println(Thread.currentThread()+"job1 exit,time->"+System.currentTimeMillis());
return 1.2;
}
});
//cf執行完成後會將執行結果和執行過程中拋出的異常傳入回調方法,如果是正常執行的則傳入的異常爲null
CompletableFuture<String> cf2=cf.handle((a,b)->{
System.out.println(Thread.currentThread()+"job2 start,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if(b!=null){
System.out.println("error stack trace->");
b.printStackTrace();
}else{
System.out.println("run succ,result->"+a);
}
System.out.println(Thread.currentThread()+"job2 exit,time->"+System.currentTimeMillis());
if(b!=null){
return "run error";
}else{
return "run succ";
}
});
//等待子任務執行完成
System.out.println("main thread start wait,time->"+System.currentTimeMillis());
//get的結果是cf2的返回值,跟cf沒關係了
System.out.println("run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其執行結果如下:
將上述示例中的if(true) 改成if(false),其輸出如下:
三、組合處理
1、thenCombine / thenAcceptBoth / runAfterBoth
這三個方法都是將兩個CompletableFuture組合起來,只有這兩個都執行完了纔會執行某個任務,區別在於,thenCombine會將兩個任務的執行結果作爲方法入參傳遞到指定方法中,且該方法有返回值;thenAcceptBoth同樣將兩個任務的執行結果作爲方法入參,但是無返回值;runAfterBoth沒有入參,也沒有返回值,注意這3個方法也各有一個以Async結尾的方法,thenCombineAsync / thenAcceptBothAsync / runAfterBothAsync,跟不帶Async的區別同thenApply 與 thenApplyAsync。 測試用例如下:
@Test
public void test7() throws Exception {
ForkJoinPool pool=new ForkJoinPool();
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
});
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return 3.2;
});
//cf和cf2的異步任務都執行完成後,會將其執行結果作爲方法入參傳遞給cf3,且有返回值
CompletableFuture<Double> cf3=cf.thenCombine(cf2,(a,b)->{
System.out.println(Thread.currentThread()+" start job3,time->"+System.currentTimeMillis());
System.out.println("job3 param a->"+a+",b->"+b);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job3,time->"+System.currentTimeMillis());
return a+b;
});
//cf和cf2的異步任務都執行完成後,會將其執行結果作爲方法入參傳遞給cf3,無返回值
CompletableFuture cf4=cf.thenAcceptBoth(cf2,(a,b)->{
System.out.println(Thread.currentThread()+" start job4,time->"+System.currentTimeMillis());
System.out.println("job4 param a->"+a+",b->"+b);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job4,time->"+System.currentTimeMillis());
});
//cf4和cf3都執行完成後,執行cf5,無入參,無返回值
CompletableFuture cf5=cf4.runAfterBoth(cf3,()->{
System.out.println(Thread.currentThread()+" start job5,time->"+System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("cf5 do something");
System.out.println(Thread.currentThread()+" exit job5,time->"+System.currentTimeMillis());
});
System.out.println("main thread start cf.get(),time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("cf run result->"+cf.get());
System.out.println("main thread start cf5.get(),time->"+System.currentTimeMillis());
System.out.println("cf5 run result->"+cf5.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其運行結果如下:
job1 和 job2幾乎同時運行,job2比job1先執行完成,等job1退出後,job3和job4幾乎同時開始運行,job4先退出,等job3執行完成,job5開始了,等job5執行完成後,主線程退出。
2、applyToEither / acceptEither / runAfterEither
這三個方法都是將兩個CompletableFuture組合起來,只要其中一個執行完了就會執行某個任務,其區別在於applyToEither會將已經執行完成的任務的執行結果作爲方法入參,並有返回值;acceptEither同樣將已經執行完成的任務的執行結果作爲方法入參,但是沒有返回值;runAfterEither沒有方法入參,也沒有返回值。測試用例如下:
@Test
public void test8() throws Exception {
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
});
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return 3.2;
});
//cf和cf2的異步任務都執行完成後,會將其執行結果作爲方法入參傳遞給cf3,且有返回值
CompletableFuture<Double> cf3=cf.applyToEither(cf2,(result)->{
System.out.println(Thread.currentThread()+" start job3,time->"+System.currentTimeMillis());
System.out.println("job3 param result->"+result);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job3,time->"+System.currentTimeMillis());
return result;
});
//cf和cf2的異步任務都執行完成後,會將其執行結果作爲方法入參傳遞給cf3,無返回值
CompletableFuture cf4=cf.acceptEither(cf2,(result)->{
System.out.println(Thread.currentThread()+" start job4,time->"+System.currentTimeMillis());
System.out.println("job4 param result->"+result);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job4,time->"+System.currentTimeMillis());
});
//cf4和cf3都執行完成後,執行cf5,無入參,無返回值
CompletableFuture cf5=cf4.runAfterEither(cf3,()->{
System.out.println(Thread.currentThread()+" start job5,time->"+System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("cf5 do something");
System.out.println(Thread.currentThread()+" exit job5,time->"+System.currentTimeMillis());
});
System.out.println("main thread start cf.get(),time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("cf run result->"+cf.get());
System.out.println("main thread start cf5.get(),time->"+System.currentTimeMillis());
System.out.println("cf5 run result->"+cf5.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其運行結果如下:
job1 和job2 同時開始運行,job2先執行完成,然後job4開始執行,理論上job3和job4應該同時開始運行,但是此時只有job4開始執行了,job3是等待job1執行完成後纔開始執行,job4先於job3執行完成,然後job5開始執行,等job5執行完成後,主線程退出。上述差異且到下篇源碼分析時再做探討。
3、thenCompose
thenCompose方法會在某個任務執行完成後,執行指定的方法,該方法會返回一個新的待執行任務,然後執行這個新任務,測試用例如下:
@Test
public void test9() throws Exception {
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
});
CompletableFuture<String> cf2= cf.thenCompose((param)->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job3,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job3,time->"+System.currentTimeMillis());
return "job3 test";
});
});
System.out.println("main thread start cf.get(),time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("cf run result->"+cf.get());
System.out.println("main thread start cf2.get(),time->"+System.currentTimeMillis());
System.out.println("cf2 run result->"+cf2.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其輸出如下:
job1執行完成後job2開始執行,等job2執行完成後會把job3返回,然後執行job3,等job3執行完成後,主線程退出。
4、allOf / anyOf
allOf返回的CompletableFuture是多個任務都執行完成後纔會執行,只有有一個任務執行異常,則返回的CompletableFuture執行get方法時會拋出異常,如果都是正常執行,則get返回null。
@Test
public void test11() throws Exception {
// 創建異步執行任務:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
return 1.2;
});
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
return 3.2;
});
CompletableFuture<Double> cf3 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" start job3,time->"+System.currentTimeMillis());
try {
Thread.sleep(1300);
} catch (InterruptedException e) {
}
// throw new RuntimeException("test");
System.out.println(Thread.currentThread()+" exit job3,time->"+System.currentTimeMillis());
return 2.2;
});
//allof等待所有任務執行完成才執行cf4,如果有一個任務異常終止,則cf4.get時會拋出異常,都是正常執行,cf4.get返回null
//anyOf是隻有一個任務執行完成,無論是正常執行或者執行異常,都會執行cf4,cf4.get的結果就是已執行完成的任務的執行結果
CompletableFuture cf4=CompletableFuture.allOf(cf,cf2,cf3).whenComplete((a,b)->{
if(b!=null){
System.out.println("error stack trace->");
b.printStackTrace();
}else{
System.out.println("run succ,result->"+a);
}
});
System.out.println("main thread start cf4.get(),time->"+System.currentTimeMillis());
//等待子任務執行完成
System.out.println("cf4 run result->"+cf4.get());
System.out.println("main thread exit,time->"+System.currentTimeMillis());
}
其輸出如下:
主線程等待最後一個job1執行完成後退出。anyOf返回的CompletableFuture是多個任務只要其中一個執行完成就會執行,其get返回的是已經執行完成的任務的執行結果,如果該任務執行異常,則拋出異常。將上述測試用例中allOf改成anyOf後,其輸出如下: