Java中CompletableFuture?的詳細(xì)介紹
1.概述
1.0 創(chuàng)建 CompletableFuture 的對象的工廠方法
static?CompletableFuture<Void> runAsync(Runnable?runnable) static?CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync 的參數(shù)是 Runnable, 返回值是 CompletableFuture, 意思是工廠方法創(chuàng)建的 CompletableFuture 對象封裝的任務(wù)沒有返回值。
例如:
CompletableFuture<Void> run = CompletableFuture.runAsync(()-> System.out.println("hello"));
而 suppyAsync 參數(shù)類型是 Supplier,返回值是CompletableFuture<U>
, 意思是任務(wù)不接受參數(shù),但是會返回結(jié)果。
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> { System.out.println("Hello"); return "hello world!""; }); System.out.println(supply.get()); //hello world!"
所以如果任務(wù)需要返回結(jié)果,那么應(yīng)該選擇 suppyAsync;否則可以選擇 runAsync。
1.1 non-async 和 async 區(qū)別
public CompletionStage<Void> thenRun(Runnable action); public CompletionStage<Void> thenRunAsync(Runnable action); public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
CompletableFuture 中有眾多類似這樣的方法,那么 non-async 和 async 和版本的方法究竟有什么區(qū)別呢? 參考官方文檔的描述:
Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.
翻譯:傳遞給 non-async 方法作為參數(shù)的函數(shù)(action)可能會在完成當(dāng)前的 CompletableFuture 的線程中執(zhí)行,也可能會在方法的調(diào)用者線程中執(zhí)行。
All async methods without an explicit Executor argument are performed using the
ForkJoinPool.commonPool()
(unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interfaceCompletableFuture.AsynchronousCompletionTask
.
翻譯:所有沒有Executor 參數(shù)的 async 方法都在 ForkJoinPool.commonPool()
線程池中執(zhí)行(除非不支持最小并發(fā)度為2,這種情況下,每個任務(wù)會創(chuàng)建一個新線程去執(zhí)行)。為了簡化監(jiān)控、調(diào)試和追蹤,所有生成的異步任務(wù)都是接口 CompletableFuture.AsynchronousCompletionTask
的實例。
從上面這兩段官方描述看。async 類方法比較容易理解,就是 CompletableFuture 實例的任務(wù)執(zhí)行完成后,會將 action 提交到缺省的異步線程池 ForkJoinPool.commonPool()
,或者 async 類方法參數(shù)Executor executor 指定的線程池中執(zhí)行。
而對于 non-async 的描述則有點不明確。action 可能會在完成 CompletableFuture 實例任務(wù)的線程中執(zhí)行,也可能會在調(diào)用 thenRun(編排任務(wù)完成后執(zhí)行 action 的系列方法) 方法的線程中執(zhí)行。這個主要是看調(diào)用 thenRun 的時候,CompletableFuture 實例的任務(wù)是否已經(jīng)完成。如果沒有完成,那么action 會在完成任務(wù)的線程中執(zhí)行。如果任務(wù)已經(jīng)完成,則 action 會在調(diào)用thenAccept 等注冊方法的線程中執(zhí)行。
1.1.1 non-async 示例:注冊 action 的時候任務(wù)可能已經(jīng)結(jié)束
@Test void testThenRunWithTaskCompleted() throws Exception{ CompletableFuture<Void> future = CompletableFuture.supplyAsync(new Supplier<Integer>() { @Override public Integer get() { System.out.println("[" + Thread.currentThread().getName() + "] " + " in task" ); return 1; } }).thenRun(() -> { System.out.println("[" + Thread.currentThread().getName() + "] " + " in action" ); }); future.get(); }
運行結(jié)果:
[ForkJoinPool.commonPool-worker-1] in task
[main] in action
分析: 任務(wù)通過 CompletableFuture.supplyAsync 提交后,會以異步的方式在 ForkJoinPool.commonPool() 線程池中運行。這時候有兩個線程,一個是[ForkJoinPool.commonPool-worker-1] 執(zhí)行 Supplier.get 方法;一個是[main] 主線程提交完異步任務(wù)后,繼續(xù)調(diào)用 thenRun 編排任務(wù)完成后執(zhí)行 action。由于Supplier.get 非常簡單,幾乎立刻返回。所以很大概率在主線程調(diào)用 thenRun 編排任務(wù)完成后執(zhí)行 action的時候,異步任務(wù)已經(jīng)完成,所以 action 在主線程中執(zhí)行了。注:在筆者的電腦上幾乎100% 是這樣的調(diào)度方式。
1.1.2 non-async 示例:注冊 action 的時候任務(wù)未完成
@Test void testThenRunWithTaskUncompleted() throws Exception{ CompletableFuture<Void> future = CompletableFuture.supplyAsync(new Supplier<Integer>() { @Override public Integer get() { System.out.println("[" + Thread.currentThread().getName() + "] " + " in task" ); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return new Random().nextInt(10); } }).thenRun(() -> { System.out.println("[" + Thread.currentThread().getName() + "] " + " in action" ); }); future.get(); }
運行結(jié)果:
[ForkJoinPool.commonPool-worker-1] in task
[ForkJoinPool.commonPool-worker-1] in action
分析:在 Supplier.get 加入 sleep,延遲任務(wù)結(jié)束。主線程提交完異步任務(wù)后,繼續(xù)調(diào)用 thenRun 編排任務(wù)完成后執(zhí)行 action 的時候,任務(wù)沒有結(jié)束。所以這個action 在完成任務(wù)的線程中執(zhí)行。
1.2 Run 類方法
Run 類方法指 thenRun、runAfterBoth、runAfterEither 等。Run 類方法的動作參數(shù)都是Runable action。例如 thenRun(Runnable action)。這就意味著這個 action 不關(guān)心任務(wù)的結(jié)果,action 本身也沒有返回值。只是為了實現(xiàn)在完成任務(wù)后執(zhí)行一個動作的目的。
1.3 Accept 類方法
Accept 類方法指 thenAccept、runAcceptBoth、runAcceptEither 等。Accept 類方法的動作參數(shù)都是Consumer<? super T> action。例如 thenAccept(Consumer<? super T> action)。如方法名和參數(shù)所示,action 是接受并消費任務(wù)結(jié)果的消費者,action 沒有返回值。
1.4 Apply 類方法
Apply 類方法指 thenApply、applyToEither 等。Apply 類方法的動作參數(shù)都是Function<? super T,? extends U> fn。例如 thenApply(Function<? super T,? extends U> fn)。如方法名和參數(shù)所示,action 將任務(wù)結(jié)果應(yīng)用函數(shù)參數(shù) fn 做轉(zhuǎn)換,返回轉(zhuǎn)換后的結(jié)果,類似于 stream 中的 map。
2 單個任務(wù)執(zhí)行完成后執(zhí)行一個動作(action)
方法 | 說明 |
---|---|
public CompletableFuture<Void> thenRun(Runnable action) thenRunAsync(Runnable action) thenRunAsync(Runnable action, Executor executor) | 任務(wù)完成后執(zhí)行 action,action 中不關(guān)心任務(wù)的結(jié)果,action 也沒有返回值 |
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) thenAcceptAsync(Consumer<? super T> action) thenAcceptAsync(Consumer<? super T> action) | 任務(wù)完成后執(zhí)行 action。如方法名和參數(shù)所示,action 是接受并消費任務(wù)結(jié)果的消費者,action 沒有返回值 |
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) thenApplyAsync(Function<? super T,? extends U> fn) thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) | 任務(wù)完成后執(zhí)行 action。action 將任務(wù)結(jié)果應(yīng)用 action 的函數(shù)參數(shù)做轉(zhuǎn)換,返回轉(zhuǎn)換后的結(jié)果,類似于 stream 中的 map |
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) | 任務(wù)完成后或者異常時運行action。action 是接受并消費任務(wù)結(jié)果的消費者,并且有返回值??梢哉J(rèn)為是支持異常處理的 thenAccept 版本。 |
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) | 任務(wù)完成后或者異常時運行action。可以認(rèn)為是支持異常處理的 thenApply 版本。 |
public CompletableFuture exceptionally(Function<Throwable, ? extends T> fn) | 如果任務(wù)或者 action 發(fā)生異常,則會觸發(fā)exceptionally的調(diào)用相當(dāng)于 try...catch |
2.0 示例 exceptionally
@Test void testCompletableFutureExceptionally(){ CompletableFuture<Integer> first = CompletableFuture .supplyAsync(() -> { if (true) { throw new RuntimeException("exception in task"); } return "hello world"; }) .thenApply(data -> { if (true) { throw new RuntimeException("exception in action"); } return 1; }) .exceptionally(e -> { System.out.println("[" + Thread.currentThread().getName() + "] " + " print exception stack trace" ); e.printStackTrace(); // 異常捕捉處理,前面兩個處理環(huán)節(jié)的日常都能捕獲 return 0; }); }
3 兩個任務(wù)執(zhí)行編排
下面表格列出的每個方法都有對應(yīng)兩個版本的 async 方法,一個有executor 參數(shù),一個沒有。為了表格盡量簡潔,表格中就不再列出async 方法了
方法 | 說明 |
---|---|
public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage> fn) | 串行組合兩個 CompletableFuture 任務(wù),后一個任務(wù)依賴前一個任務(wù)的結(jié)果,后一個任務(wù)可以返回與第一個任務(wù)不同類型的返回值。執(zhí)行后一個任務(wù)的線程與前面討論 action 執(zhí)行的線程類似。 |
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action) | 這里有兩個并行任務(wù),一個是runAfterBoth 調(diào)用本身所屬的CompletableFuture 實例A,一個是參數(shù) other 引用的任務(wù)B。兩個并行任務(wù)都完成后執(zhí)行 action,action 中不關(guān)心任務(wù)的結(jié)果,action 也沒有返回值 |
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) | 與runAfterBoth 類似,也有兩個并行任務(wù) A 和 B。兩個并行任務(wù)都完成后執(zhí)行 action。如方法名和參數(shù)所示,action 是接受并消費兩個任務(wù)結(jié)果的消費者,action 沒有返回值 |
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) | 與runAfterBoth 類似,也有兩個并行任務(wù) A 和 B。兩個并行任務(wù)都完成后執(zhí)行 action,action 依賴兩個任務(wù)的結(jié)果,并對結(jié)果做轉(zhuǎn)換,返回一個新值 |
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action) | 與runAfterBoth 類似,也有兩個并行任務(wù) A 和 B。兩個并行任務(wù)任意一個完成后執(zhí)行 action,action 中不關(guān)心任務(wù)的結(jié)果,action 也沒有返回值 |
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) | 與runAfterBoth 類似,也有兩個并行任務(wù) A 和 B。兩個并行任務(wù)任意一個完成后執(zhí)行 action。如方法名和參數(shù)所示,action 是接受并消費兩個任務(wù)結(jié)果的消費者,action 沒有返回值 |
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) | 與runAfterBoth 類似,也有兩個并行任務(wù) A 和 B。兩個并行任務(wù)任意一個完成后執(zhí)行 action。action 將任務(wù)結(jié)果應(yīng)用 action 的函數(shù)參數(shù)做轉(zhuǎn)換,返回轉(zhuǎn)換后的結(jié)果,類似于 stream 中的 map |
4 多個任務(wù)執(zhí)行編排
下面表格列出的每個方法都有對應(yīng)兩個版本的 async 方法,一個有executor 參數(shù),一個沒有。為了表格盡量簡潔,表格中就不再列出async 方法了
方法 | 說明 |
---|---|
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) | 所有參數(shù)引用的任務(wù)都完成后,才觸發(fā)執(zhí)行當(dāng)前的 CompletableFuture 實例任務(wù)。 |
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) | 參數(shù)引用的任務(wù)中任何一個完成后,就會觸發(fā)執(zhí)行當(dāng)前的 CompletableFuture 實例任務(wù)。 |
5 CompletableFuture 其他方法
方法 | 說明 |
---|---|
public boolean cancel(boolean mayInterruptIfRunning) | 如果任務(wù)未完成,以 CancellationException 異常結(jié)束任務(wù)。 |
public boolean isCancelled() | 判斷任務(wù)是否取消。 |
public T join() | 阻塞等待 獲取返回值 |
public T get() throws InterruptedException, ExecutionException public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException | 阻塞等待(有超時重載版本)獲取返回值。get 與 join區(qū)別,get 會拋出 checked 異常,調(diào)用代碼需要處理異常。join 沒有超時重載版本。 |
public T getNow(T valueIfAbsent) | 獲取返回值,如果任務(wù)未完成則返回valueIfAbsent 參數(shù)指定value |
public boolean isDone() | 任務(wù)是否執(zhí)行完成 |
到此這篇關(guān)于Java中CompletableFuture 的詳細(xì)介紹的文章就介紹到這了,更多相關(guān)CompletableFuture 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中CountDownLatch工具類詳細(xì)解析
這篇文章主要介紹了Java中CountDownLatch工具類詳細(xì)解析,創(chuàng)建CountDownLatch對象時,會傳入一個count數(shù)值,該對象每次調(diào)用countDown()方法會使count?--?,就是count每次減1,需要的朋友可以參考下2023-11-11如何實現(xiàn)自定義SpringBoot的Starter組件
這篇文章主要介紹了實現(xiàn)自定義SpringBoot的Starter組件的示例代碼,想要自定義starter組件,首先要了解springboot是如何加載starter的,也就是springboot的自動裝配機制原理,本文結(jié)合示例代碼詳細(xì)講解,需要的朋友可以參考下2023-02-02SpringCloud?Feign?傳輸Date類型參數(shù)存在誤差的問題
這篇文章主要介紹了SpringCloud?Feign?傳輸Date類型參數(shù)存在誤差的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Spring通過三級緩存解決循環(huán)依賴問題的過程詳解
循環(huán)依賴指的是在對象之間存在相互依賴關(guān)系,形成一個閉環(huán),導(dǎo)致無法準(zhǔn)確地完成對象的創(chuàng)建和初始化,本文主要介紹了Spring通過三級緩存解決循環(huán)依賴的方法,需要的可以參考下2023-10-10JAVA多線程之實現(xiàn)用戶任務(wù)排隊并預(yù)估排隊時長
本文主要介紹了Java多線程之實現(xiàn)用戶任務(wù)排隊并預(yù)估排隊時長的問題,文中的代碼具有一定的學(xué)習(xí)和工作價值,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下吧2021-12-12Springboot之@Async不執(zhí)行原因及分析
這篇文章主要介紹了Springboot之@Async不執(zhí)行原因及分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09淺談Java8 的foreach跳出循環(huán)break/return
這篇文章主要介紹了Java8 的foreach跳出循環(huán)break/return,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07