深入學(xué)習(xí)java8?中的CompletableFuture
1 前言
在項(xiàng)目開(kāi)發(fā)中,異步化處理是非常常見(jiàn)的解決問(wèn)題的手段,異步化處理除了使用線程池之外,還可以使用 CompletableFuture
來(lái)實(shí)現(xiàn),在多任務(wù)處理且之間存在邏輯關(guān)系的情況下,就能夠體現(xiàn)出其巨大的優(yōu)勢(shì)和靈活性。CompletableFuture
底層使用的是 ForkJoinPool
線程池來(lái)實(shí)現(xiàn)線程的執(zhí)行和調(diào)度。
2 簡(jiǎn)單使用
在使用線程池時(shí),通常的使用方法如下所示:
ExecutorService service = Executors.newFixedThreadPool(3); Callable<String> task1 = () ->{return "task1";}; Runnable task2 = () ->{ System.out.println("task2 "); }; // 用于提交任務(wù)根據(jù)是否獲取返回值分為 Callable 和 Runnable,分別使用 submit 和 execute 方法 service.submit(task1); service.execute(task2);
但是在 CompletableFuture
中,使用方法還是有所區(qū)別的,是線程池和任務(wù)的結(jié)合,能夠使用鏈?zhǔn)骄幊虂?lái)處理任務(wù)之間的邏輯關(guān)系。
具體的使用如下所示:
// 使用默認(rèn)線程池 CompletableFuture<String> async1 = CompletableFuture.supplyAsync(() -> { log.info("async1 ... "); return "async1"; }); // 使用自定義線程池 CompletableFuture<String> async1 = CompletableFuture.supplyAsync(() -> { log.info("async1 ... "); return "async1"; }, Executors.newSingleThreadExecutor()); // runAsync 的使用方式 CompletableFuture<Void> future = CompletableFuture.runAsync(()-> { System.out.println("runAsync"); });
異步任務(wù)的開(kāi)啟一般有兩個(gè)方法,supplyAsync
和 runAsync
,這兩個(gè)方法的別別在于:
- 1 supplyAsync 不接受入?yún)?,但是?huì)有返回結(jié)果。
- 2 runAsync 也是不接受入?yún)?,但是沒(méi)有返回結(jié)果。
這里需要先說(shuō)明一下,xxxAsync 的方法都是從使用線程池中獲取一個(gè)線程來(lái)處理任務(wù),不帶 Async 結(jié)尾的方法則是使用上一任務(wù)的線程繼續(xù)處理。
3 異步處理
在正式開(kāi)始之前,需要講解一下 java8 函數(shù)式編程的函數(shù),相信大家看到這么多的函數(shù)都會(huì)頭暈的,但是其中也是有規(guī)律可循的,先說(shuō)三個(gè)主要的:
- 1 Function , 既然是函數(shù),那么就會(huì)有一個(gè)入?yún)⒑头祷刂?,可以用于?jì)算。
- 2 Comsumer , 是一個(gè)消費(fèi)者,接收一個(gè)入?yún)⒌菦](méi)有返回值,只用于消費(fèi)。
- 3 Supplier, 是一個(gè)提供者,不接受參數(shù),但是有一個(gè)返回值,可以用于對(duì)象的創(chuàng)建。
- 4 Predicate, 用來(lái)做判斷使用,接收一個(gè)入?yún)?返回值是 布爾類型的,true 或者 false。
簡(jiǎn)單的案例如下圖所示:
有這基本的 4 個(gè),就可以進(jìn)行延伸了,比如 IntFunction 則是接收一個(gè) int 類型的參數(shù),處理完成后即可返回,前面的 Int 只是規(guī)定了入?yún)⒌念愋投?,再?BiConsumer , 則是接收兩個(gè)入?yún)?,Consumer 則是只能接收一個(gè)參數(shù)。依次類推就可以知道所有的函數(shù)式接口的功能,是不是很簡(jiǎn)單?
3.1 thenApply
thenApply 和 thenApplyAsync 都是接收一個(gè) Function 參數(shù),即接收一個(gè)參數(shù)并返回結(jié)果。區(qū)別在于前者是使用前一個(gè)任務(wù)的線程繼續(xù)處理,后者是從線程池中在獲取一個(gè)線程處理任務(wù)。
如上圖所示,thenApply 的任務(wù)處理和 future 使用的是一個(gè)線程,但是 thenApplyAsync 就換了一個(gè)線程繼續(xù)數(shù)據(jù)的處理。
3.2 thenAccept 和 thenRun
從方法名可以看到 thenAccept 和 thenRun 都是使用前一個(gè)人任務(wù)的線程進(jìn)行處理的。兩者都是在前一個(gè)任務(wù)完成后進(jìn)行處理,區(qū)別點(diǎn)在于 thenAccept 接收的是一個(gè) Consumer , 而 thenRun 接收的是一個(gè) Runnable, 因此兩者都沒(méi)有返回值,但是前者可以接收并消費(fèi)一個(gè)參數(shù),但是 thenRun 不能接收參數(shù)。這兩個(gè)方法的測(cè)試如下圖所示:
既然這兩個(gè)方法已經(jīng)搞清楚了,那么 thenAcceptAsync 和 thenRunAsync 是不是就順手學(xué)到了呢?異步編程的 API 真的是很簡(jiǎn)單。
3.3 exceptionally 異常處理
exceptionally 屬于異常處理流程,如果發(fā)生異常則需要進(jìn)行異常處理,需要將異常最為參數(shù)傳遞給 exceptionally, 而其需要的是一個(gè) Function 參數(shù),這里的異常處理也是同步進(jìn)行的,也是采用上一個(gè)任務(wù)的線程進(jìn)行處理。
// 拋出異常信息 CompletableFuture<String> exceptionally = future.exceptionally((ex) -> { log.info("error information " + ex.getLocalizedMessage()); return ex.getMessage(); });
3.4 whenComplete 方法完成之后
這個(gè)方法是當(dāng)某個(gè)任務(wù)執(zhí)行完成之后進(jìn)行回調(diào),會(huì)將任務(wù)的執(zhí)行結(jié)果或者執(zhí)行期間的異常信息傳遞過(guò)來(lái)進(jìn)行處理,在正常的情況下,異常信息為 null,能夠得到任務(wù)的運(yùn)算結(jié)果,異常情況下,異常信息不為空,返回結(jié)果為 null。這里的 whenComplete 接受的是一個(gè) BiConsumer 函數(shù),也就是兩個(gè)入?yún)ⅲ瑳](méi)有返回結(jié)果,一個(gè)是方法的返回結(jié)果,一個(gè)則是任務(wù)處理過(guò)程中的異常信息。
// 返回結(jié)果 CompletableFuture<String> whenComplete = future.whenComplete((res, ex) -> { if (StrUtil.isNotBlank(res)) { log.info("task execute result {}", res); } if (res != null) { log.info("task error info {}", ex.getMessage()); } });
知道了 whenComplete 方法,那么 whenCompleteAsync 方法的使用就知道了,就是異步處理了。
3.5 handle
handle 的使用和 whenComplete 方法類似,都是獲取任務(wù)的結(jié)果,只不過(guò) handle 有返回結(jié)果,接受的參數(shù)是一個(gè) BiFunction ,那么具體的使用方法如下圖所示:
// handle 處理返回結(jié)果 CompletableFuture<String> handle = future.handle((res, ex) -> { if (StrUtil.isNotBlank(res)) { log.info("task execute result {}", res); return "handle result exception"; } if (res != null) { log.info("task error info {}", ex.getMessage()); } return "handle result"; });
通過(guò)以上的分析,我們已經(jīng)到得了以下規(guī)律:任何一個(gè)方法的實(shí)現(xiàn)都有三個(gè)類似的 API,一個(gè)是同步處理,一個(gè)是異步處理,一個(gè)是異步處理并指定線程池參數(shù)。目前已經(jīng)介紹了 6 個(gè) API,分別是 thenApply
, thenAccept
,thenRun
, whenComplete
, handle
和 一個(gè)異常處理 exceptionally
, 前五個(gè)舉一反三就知道了其他的兩個(gè)異步調(diào)用 API,掌握了其中的規(guī)律就不會(huì)覺(jué)得很多,無(wú)非就是同步異步,是否接收參數(shù)和有無(wú)返回值的區(qū)別。
4 處理組合
4.1 任務(wù)均完成后組合
thenCombine
、thenAcceptBoth
、runAfterBoth
這三個(gè)方法都是在兩個(gè) CompletableFuture
任務(wù)結(jié)束后在進(jìn)行執(zhí)行,區(qū)別在于是否接受參數(shù)以及是否有返回值,如圖所示查看其接受的參數(shù)。
- 1
thenCombine
方法為兩個(gè),第一個(gè)為CompletionStage
對(duì)象即另一個(gè)異步任務(wù),第二個(gè)為BiFunction
,接收兩個(gè)任務(wù)的處理結(jié)果并返回處理結(jié)果。 - 2
thenAcceptBoth
方法為兩個(gè),第一個(gè)為CompletionStage
對(duì)象即另一個(gè)異步任務(wù),第二個(gè)為BiConsumer
, 接收兩個(gè)任務(wù)的處理結(jié)果不過(guò)沒(méi)有返回值。 - 3
runAfterBoth
方法為兩個(gè),第一個(gè)為CompletionStage
對(duì)象即另一個(gè)異步任務(wù),第二個(gè)為Runnable
,不接收兩個(gè)任務(wù)的處理結(jié)果,也沒(méi)有返回值。
下圖是方法的使用案例:
既然知道了這些方法的用法,那么
thenCombineAsync
、thenAcceptBothAsync
、runAfterBothAsync
是不是就可以同理掌握了呢?
4.2 任一任務(wù)完成
前文提到的都是兩個(gè)任務(wù)均完成的情況,接下來(lái)的三個(gè)方法則是任何一個(gè)任務(wù)完成即可執(zhí)行下一個(gè)動(dòng)作,applyToEither
、acceptEither
、runAfterEither
這三個(gè)方法都是在兩個(gè)異步任務(wù)執(zhí)行結(jié)果之后的處理,任何一個(gè)任務(wù)執(zhí)行完畢之后就進(jìn)行繼續(xù)處理。
這里的任一任務(wù)執(zhí)行完成和兩者任務(wù)都執(zhí)行完在執(zhí)行是類似的,區(qū)別在于這里接收的是一個(gè)參數(shù):
- 1 applyToEither 接收的參數(shù)是 CompletionStage 和 Function。
- 2 acceptEither 接收的參數(shù)是 CompletionStage 和 Consumer。
- 3 runAfterEither 接收的參數(shù)是 CompletionStage 和 Runnable。
這里已經(jīng)學(xué)習(xí)到了 applyToEither
、acceptEither
、runAfterEither
三個(gè)方法,那么類似的 applyToEitherAsync
、acceptEitherAsync
、runAfterEitherAsync
也可以知道其具體用法。
4.3 任務(wù)處理結(jié)果
thenCompose
的用法和 thenCombine
等的用法基本都是一樣的,只不過(guò)在返回參數(shù)上有所區(qū)別,結(jié)果是返回一個(gè) Future, 入?yún)⑹且粋€(gè) Function 。在了解了 thenCompose
之后,那么 thenComposeAsync
的使用方法就是類似了。
CompletableFuture<String> thenCompose = future.thenCompose((res) -> { log.info("result is {}", res); return CompletableFuture.supplyAsync(() -> { log.info("supplyAsync"); return "result"; }); });
4.4 所有或者任何
前面已經(jīng)分享過(guò)了兩個(gè)任務(wù)和一個(gè)任務(wù)的處理之后的操作,在本節(jié)中將分享 allOf
和 anyOf
,這是多個(gè)任務(wù)的聚合處理,入?yún)⒍际嵌鄠€(gè) CompletableFuture
, 區(qū)別在于是任何一個(gè)任務(wù)完成后就執(zhí)行后續(xù)任務(wù),還是所有的任務(wù)都完成后再繼續(xù)任務(wù)處理。
其使用方法如下所示:
CompletableFuture<Void> allOf = CompletableFuture.allOf(future); CompletableFuture<Object> andOf = CompletableFuture.anyOf(future);
5 總結(jié)
文中,我們首先介紹了函數(shù)式編程的接口使用方法,然后分享了 CompletableFuture
的 API 使用方法。核心就是函數(shù)式編程接口,接收的是 Function
、Consumer
還是 Runable
, 其次就是否是 xxxAsync
異步處理。
到此這篇關(guān)于深入學(xué)習(xí)java8 中的CompletableFuture的文章就介紹到這了,更多相關(guān)java8 CompletableFuture 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)計(jì)模塊系列之書(shū)店管理系統(tǒng)單機(jī)版(一)
這篇文章主要為大家詳細(xì)介紹了Java單機(jī)版的書(shū)店管理系統(tǒng)設(shè)計(jì)模塊和思想第一章,感興趣的小伙伴們可以參考一下2016-08-08spring boot 配置Filter過(guò)濾器的方法
本篇文章主要介紹了spring boot 配置Filter過(guò)濾器的方法,實(shí)例分析了spring boot 配置Filter過(guò)濾器的技巧,有興趣的可以了解一下。2017-03-03SpringBoot使用攔截器Interceptor實(shí)現(xiàn)統(tǒng)一角色權(quán)限校驗(yàn)
角色權(quán)限校驗(yàn),是保證接口安全必備的能力:有權(quán)限才可以操作,所以,一般對(duì)于這種通用邏輯,推薦不與主業(yè)務(wù)邏輯耦合,那么怎么來(lái)解耦,那么本文小編就給大家詳細(xì)講解如何使用攔截器Interceptor實(shí)現(xiàn)統(tǒng)一角色權(quán)限校驗(yàn),需要的朋友可以參考下2023-07-07在springboot中注入FilterRegistrationBean不生效的原因
這篇文章主要介紹了在springboot中注入FilterRegistrationBean不生效的原因及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Spring Boot jpa Service層代碼實(shí)例
這篇文章主要介紹了Spring Boot jpa Service層代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java雜談之類和對(duì)象 封裝 構(gòu)造方法以及代碼塊詳解
在現(xiàn)實(shí)世界中,真實(shí)存在的東西,比如吉普車(chē),卡丁車(chē),貨車(chē)。我們?cè)谡J(rèn)識(shí)它的時(shí)候就會(huì)在腦海中將它抽象為一種類別叫做車(chē)。 好了,那再計(jì)算機(jī)世界中,它同樣的也會(huì)這樣做2021-09-09