CompletableFuture?異步編排示例詳解
從Future聊起
Future
是java 1.5引入的異步編程api,它表示一個(gè)異步計(jì)算結(jié)果,提供了獲取異步結(jié)果的能力,解決了多線程場(chǎng)景下Runnable
線程任務(wù)無(wú)法獲取結(jié)果的問(wèn)題。
但是其獲取異步結(jié)果的方式并不夠優(yōu)雅,我們必須使用Future.get的方式阻塞調(diào)用線程,或者使用輪詢方式判斷 Future.isDone 任務(wù)是否結(jié)束,再獲取結(jié)果。
public interface Future<V> { //任務(wù)是否完成 boolean isDone(); //阻塞調(diào)用線程獲取異步結(jié)果 V get() throws InterruptedException, ExecutionException; //在指定時(shí)間內(nèi)阻塞線程獲取異步結(jié)果 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
假如存在多個(gè)異步任務(wù)相互依賴,一個(gè)或多個(gè)異步線程任務(wù)需要依賴上一個(gè)異步線程任務(wù)結(jié)果,并且多個(gè)異步任務(wù)能夠組合結(jié)果,顯然這種阻塞線程的方式并不能優(yōu)雅解決。
我們更希望能夠提供一種異步回調(diào)的方式,組合各種異步任務(wù),而無(wú)需開(kāi)發(fā)者對(duì)多個(gè)異步任務(wù)結(jié)果的監(jiān)聽(tīng)編排。
為了解決優(yōu)化上述問(wèn)題,java8 新增了CompletableFuture
API ,其大大擴(kuò)展了Future
能力,并提供了異步任務(wù)編排能力。
CompletableFuture
CompletableFuture
實(shí)現(xiàn)了新的接口CompletionStage
,并擴(kuò)展了Future
接口。查看類(lèi)圖
創(chuàng)建異步任務(wù)
CompletableFuture
提供了四種方法去創(chuàng)建一個(gè)異步任務(wù)。
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
:創(chuàng)建一個(gè)有返回值的異步任務(wù)實(shí)例static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
:創(chuàng)建一個(gè)有返回值的異步任務(wù)實(shí)例,可以指定線程池static CompletableFuture<Void> runAsync(Runnable runnable)
:創(chuàng)建一個(gè)無(wú)返回值的任務(wù)實(shí)例static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
:創(chuàng)建一個(gè)無(wú)返回值的任務(wù)實(shí)例,允許指定線程池
著幾個(gè)方法本質(zhì)上是有返回值和無(wú)返回值兩種類(lèi)型方法,supply方法可以獲取異步結(jié)果,而run方法則無(wú)返回值,根據(jù)需要使用。
同時(shí)兩種類(lèi)型的方法均提供了指定線程池的重載,如果不指定線程池會(huì)默認(rèn)使用ForkJoinPool.commonPool()
,默認(rèn)線程數(shù)為cpu核心數(shù),建議使用自定義線程池的方式,避免線程資源競(jìng)爭(zhēng)
一個(gè)簡(jiǎn)單樣例
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> { System.out.println("無(wú)返回值任務(wù)"); }); runAsync.get(); CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> "hello completableFuture"); String result = supplyAsync.get(); System.out.println(result);
我們依然可以通過(guò)get()
方法阻塞獲取異步結(jié)果任務(wù),但是CompletableFuture
主要還是用于異步回調(diào)及異步任務(wù)編排使用。
異步回調(diào)
在任務(wù)執(zhí)行結(jié)束后我們希望能夠自動(dòng)觸發(fā)回調(diào)方法,CompletableFuture
提供了兩種方法實(shí)現(xiàn)。
CompletableFuture<T> whenComplete( BiConsumer<? super T, ? super Throwable> action)
:當(dāng)上一階段任務(wù)執(zhí)行結(jié)束后,回調(diào)方法接受上一階段結(jié)果或者異常,返回上一階段任務(wù)結(jié)果<U> CompletableFuture<U> handle( BiFunction<? super T, Throwable, ? extends U> fn)
:當(dāng)上一階段任務(wù)執(zhí)行結(jié)束后,回調(diào)方法接受上一階段結(jié)果或者異常,并最終返回回調(diào)方法處理結(jié)果CompletableFuture<T> exceptionally( Function<Throwable, ? extends T> fn)
:上一階段任務(wù)出現(xiàn)異常后的回調(diào),返回結(jié)果是回調(diào)函數(shù)的返回結(jié)果。
whenComplete 與 handle 區(qū)別:兩者均接受上一階段任務(wù)結(jié)果或異常,但是whenComplete 回調(diào)中沒(méi)有返回值,所以其結(jié)果是上一階段任務(wù),而handle 最終返回的是其回調(diào)方法方法,其主要是BiConsumer
與BiFunction
的區(qū)別。
異步編排
CompletionStage
表示異步計(jì)算的一個(gè)階段,當(dāng)一個(gè)計(jì)算處理完成后會(huì)觸發(fā)其他依賴的階段。當(dāng)然一個(gè)階段的觸發(fā)也可以是由多個(gè)階段的完成觸發(fā)或者多個(gè)中的任意一個(gè)完成觸發(fā)。該接口定義了異步任務(wù)編排的各種場(chǎng)景,CompletableFuture
則實(shí)現(xiàn)了這些場(chǎng)景。
可以把這些場(chǎng)景大致分為三類(lèi):串行、AND和OR。下面會(huì)逐個(gè)分析各個(gè)場(chǎng)景,接口中定義的以Async
結(jié)尾的方法,指下一階段任務(wù)會(huì)被單獨(dú)提交到線程池中執(zhí)行,后面不在贅述。
串行
當(dāng)上一階段任務(wù)執(zhí)行完畢后,繼續(xù)提交執(zhí)行其他任務(wù)
<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
:接收上一階段任務(wù)結(jié)果,并可獲取返回值。CompletableFuture<Void> thenAccept(Consumer<? super T> action)
:接收上一階段任務(wù)結(jié)果,無(wú)返回值。CompletableFuture<Void> thenRun(Runnable action)
:不接收上一階段任務(wù)結(jié)果,并且無(wú)返回值。
T:上一個(gè)任務(wù)返回結(jié)果的類(lèi)型 U:當(dāng)前任務(wù)的返回值類(lèi)型
AND
組合多個(gè)異步任務(wù),當(dāng)多個(gè)任務(wù)執(zhí)行完畢繼續(xù)執(zhí)行其他任務(wù)
<U,V> CompletableFuture<V> thenCombine( CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
:上一階段任務(wù)與other任務(wù)均執(zhí)行結(jié)束,接收兩個(gè)任務(wù)的結(jié)果,并可獲取返回值<U> CompletableFuture<U> thenCompose( Function<? super T, ? extends CompletionStage<U>> fn)
: 使用上一階段任務(wù)的結(jié)果,返回一個(gè)新的CompletableFuture
實(shí)例<U> CompletableFuture<Void> thenAcceptBoth( CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
:上一階段任務(wù)與other任務(wù)均執(zhí)行結(jié)束,接收兩個(gè)任務(wù)的結(jié)果,無(wú)返回值CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
:上一階段任務(wù)與other任務(wù)均執(zhí)行結(jié)束,不接收兩個(gè)任務(wù)的結(jié)果,無(wú)返回值static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
:等待所有異步任務(wù)執(zhí)行結(jié)束
T:上一個(gè)任務(wù)返回結(jié)果的類(lèi)型 U:上一個(gè)other任務(wù)的返回值類(lèi)型 V:當(dāng)前任務(wù)返回值
OR
當(dāng)多個(gè)任務(wù)中任意任務(wù)執(zhí)行完成則繼續(xù)執(zhí)行其他任務(wù)。
<U> CompletableFuture<U> applyToEither( CompletionStage<? extends T> other, Function<? super T, U> fn)
: 接收上一階段任務(wù)與other任務(wù)最快執(zhí)行完成的結(jié)果,并可獲取返回值CompletableFuture<Void> acceptEither( CompletionStage<? extends T> other, Consumer<? super T> action)
:接收上一階段任務(wù)與other任務(wù)最快執(zhí)行完成的結(jié)果,無(wú)返回值CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
:上一階段任務(wù)與other任務(wù)任意任務(wù)完成執(zhí)行,不接收結(jié)果,無(wú)返回值static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
:組合多個(gè)任務(wù),返回最快執(zhí)行結(jié)束的任務(wù)結(jié)果
Future 機(jī)制擴(kuò)展
CompletableFuture
不僅實(shí)現(xiàn)了Future
接口,同時(shí)對(duì)其進(jìn)行了擴(kuò)展,提供了更加優(yōu)雅的實(shí)現(xiàn)。
T join()
:與get()
方法用法一致,阻塞調(diào)用線程獲取結(jié)果,但是不會(huì)拋出具體異常,簡(jiǎn)化了使用上下文T getNow(T valueIfAbsent)
:當(dāng)任務(wù)結(jié)束返回任務(wù)結(jié)果,否則返回給定的結(jié)果valueIfAbsent。boolean complete(T value)
:當(dāng)任務(wù)未結(jié)束時(shí)設(shè)置給定的結(jié)果value并結(jié)束任務(wù),已結(jié)束的任務(wù)不會(huì)生效。boolean completeExceptionally(Throwable ex)
:當(dāng)任務(wù)未結(jié)束時(shí)設(shè)置異常結(jié)果并結(jié)束任務(wù),已結(jié)束的任務(wù)不會(huì)生效
CompletableFuture 實(shí)踐
我們通過(guò)CompletableFuture
實(shí)現(xiàn)一個(gè)經(jīng)典的燒水程序。
我們可以把這個(gè)流程分為三個(gè)異步任務(wù)。
任務(wù)1:洗水壺->燒水
任務(wù)2:洗水壺->洗茶杯->拿茶葉
任務(wù)3:泡茶,需要等待任務(wù)1與任務(wù)2結(jié)束。
通過(guò)代碼模擬實(shí)現(xiàn)
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> { System.out.println("洗水壺"); try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } return "水壺"; }).thenApply(e->{ System.out.println("燒水"); try { Thread.sleep(5000); } catch (InterruptedException ex) { ex.printStackTrace(); } return "熱水"; }); //洗水壺->洗水杯->拿茶葉 CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> { System.out.println("洗茶壺"); try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } return "茶壺"; }).thenApply(e->{ try { Thread.sleep(2000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("洗水杯"); return "水杯"; }).thenApply(e->{ System.out.println("拿茶葉"); return "茶葉"; }); //泡茶 CompletableFuture<String> task3 = task1.thenCombine(task2, (a, b) -> { System.out.println("泡茶"); return "茶"; }); String tea = task3.join(); System.out.println(tea);
以上就是CompletableFuture 異步編排示例詳解的詳細(xì)內(nèi)容,更多關(guān)于CompletableFuture 異步編排的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
沒(méi)有外網(wǎng)IDEA離線使用maven倉(cāng)庫(kù)的方法
這篇文章主要介紹了沒(méi)有外網(wǎng),IDEA如何離線使用maven倉(cāng)庫(kù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08使用maven-archetype-plugin現(xiàn)有項(xiàng)目生成腳手架的方法
這篇文章主要介紹了使用maven-archetype-plugin現(xiàn)有項(xiàng)目生成腳手架的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11struts2攔截器_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
如何使用struts2攔截器,或者自定義攔截器。下面通過(guò)實(shí)例代碼給大家分享struts2攔截器的相關(guān)知識(shí),感興趣的朋友參考下吧2017-09-09SpringBoot自定義加載yml實(shí)現(xiàn)方式,附源碼解讀
這篇文章主要介紹了SpringBoot自定義加載yml實(shí)現(xiàn)方式附源碼解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03MVC AOP面向切面編程簡(jiǎn)單介紹及實(shí)例
這篇文章主要介紹了MVC AOP面向切面編程簡(jiǎn)單介紹及實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-12-12淺談HTTP使用BASIC認(rèn)證的原理及實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇淺談HTTP使用BASIC認(rèn)證的原理及實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11詳解Spring boot+CXF開(kāi)發(fā)WebService Demo
這篇文章主要介紹了詳解Spring boot+CXF開(kāi)發(fā)WebService Demo,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05Java關(guān)鍵字詳解之final static this super的用法
this用來(lái)調(diào)用目前類(lèi)自身的成員變量,super多用來(lái)調(diào)用父類(lèi)的成員,final多用來(lái)定義常量用的,static定義靜態(tài)變量方法用的,靜態(tài)變量方法只能被類(lèi)本身調(diào)用,下文將詳細(xì)介紹,需要的朋友可以參考下2021-10-10Java語(yǔ)言基于無(wú)向有權(quán)圖實(shí)現(xiàn)克魯斯卡爾算法代碼示例
這篇文章主要介紹了Java語(yǔ)言基于無(wú)向有權(quán)圖實(shí)現(xiàn)克魯斯卡爾算法代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11