completableFuture是JDK1.8版本新引入的類。下面是這個(gè)類:
實(shí)現(xiàn)了倆接口,本身是個(gè)class。這個(gè)是Future的實(shí)現(xiàn)類,使用completionStage接口去支持完成時(shí)觸發(fā)的函數(shù)和操作。
一個(gè)completetableFuture就代表了一個(gè)任務(wù),他能用Future的方法,還能做一些之前說的executorService配合futures做不了的。
之前future需要等待isDone為true才能知道任務(wù)跑完了,或者就是用get方法調(diào)用的時(shí)候會(huì)出現(xiàn)阻塞,而使用completableFuture的使用就可以用then,when等等操作來防止以上的阻塞和輪詢isDone的現(xiàn)象出現(xiàn)。
1.創(chuàng)建CompletableFuture直接new對(duì)象。
一個(gè)completableFuture對(duì)象代表著一個(gè)任務(wù),這個(gè)對(duì)象能跟這個(gè)任務(wù)產(chǎn)生聯(lián)系。
下面用的complete方法意思就是這個(gè)任務(wù)完成了需要返回的結(jié)果,然后用get()方法可以獲取到。
2.JDK1.8使用的接口類。
在本文的CompletableFuture中大量的使用了這些函數(shù)式接口。
注:這些聲明大量應(yīng)用于方法的入?yún)⒅?,像thenApply和thenAccept這倆就是一個(gè)用Function一個(gè)用Consumer
而lambda函數(shù)正好是可以作為這些接口的實(shí)現(xiàn)。例如 s->{return 1;} 這個(gè)就相當(dāng)于一個(gè)Function。因?yàn)橛腥雲(yún)⒑头祷亟Y(jié)果。
(1)Function
(2)Consumer
對(duì)于前面有Bi的就是這樣的,BiConsumer就是兩個(gè)參數(shù)的。
(3)Predicate這個(gè)接口聲明是一個(gè)入?yún)?,返回一個(gè)boolean。
(4)supplier
3.下面是這個(gè)類的靜態(tài)方法
帶有Async就是異步執(zhí)行的意思、也是一個(gè)completableFuture對(duì)象代表著一個(gè)任務(wù)這個(gè)原則。
這種異步方法都可以指定一個(gè)線程池作為任務(wù)的運(yùn)行環(huán)境,如果沒有指定就會(huì)使用ForkJoinPool線程池來執(zhí)行
(1)supplyAsync&runAsync的使用例子。
publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{ ExecutorServiceexecutorService=Executors.newCachedThreadPool(); executorService.submit(newCallable
這些任務(wù)中帶有supply是持有返回值的,run是void返回值的,在玩supply時(shí)發(fā)現(xiàn)一個(gè)問題:如果使用supplyAsync任務(wù)時(shí)不使用任務(wù)的返回值,即不用get方法阻塞主線程會(huì)導(dǎo)致任務(wù)執(zhí)行中斷。
注:跟get方法無關(guān),后面有答案
然后我開始探索是否是只有supplyAsync是這樣。我測(cè)試了runAsync發(fā)現(xiàn)也是這樣。
下圖為與supplyAsync任務(wù)執(zhí)行不全面一樣的問題,我甚至測(cè)試了將lambda換成runnable發(fā)現(xiàn)無濟(jì)于事。
答案:
造成這個(gè)原因是因?yàn)镈aemon。因?yàn)閏ompletableFuture這套使用異步任務(wù)的操作都是創(chuàng)建成了守護(hù)線程,那么我們沒有調(diào)用get方法不阻塞這個(gè)主線程的時(shí)候。主線程執(zhí)行完畢,所有線程執(zhí)行完畢就會(huì)導(dǎo)致一個(gè)問題,就是守護(hù)線程退出。
那么我們沒有執(zhí)行的代碼就是因?yàn)橹骶€程不再跑任務(wù)而關(guān)閉導(dǎo)致的,可能這個(gè)不叫問題,因?yàn)樵陂_發(fā)中我們主線程常常是一直開著的。但是這個(gè)小問題同樣讓我想了好久。
下面我們開一個(gè)非守護(hù)線程,可以看到程序執(zhí)行順利。
下面證實(shí)守護(hù)線程在其他非守護(hù)線程全部退出的情況下不繼續(xù)執(zhí)行。
finalCompletableFuturecompletableFuture=CompletableFuture.supplyAsync(()->{ System.out.println("thisislambdasupplyAsync"); System.out.println("supplyAsync是否為守護(hù)線程"+Thread.currentThread().isDaemon()); try{ TimeUnit.SECONDS.sleep(1); try(BufferedWriterwriter=newBufferedWriter (newOutputStreamWriter(newFileOutputStream(newFile("/Users/zhangyong/Desktop/temp/out.txt"))))){ writer.write("thisiscompletableFuturedaemontest"); }catch(Exceptione){ System.out.println("exceptionfind"); } }catch(InterruptedExceptione){ e.printStackTrace(); } System.out.println("thislambdaisexecutedbyforkJoinPool"); return"result1"; });
這個(gè)代碼就是操作本地文件,并且sleep了一秒。其他線程就一句控制臺(tái)輸出的代碼,最終的結(jié)果是文件沒有任何變化。
當(dāng)我把主線程sleep 5秒時(shí),本地文件會(huì)寫入一句 this is completableFuture daemon test 驗(yàn)證成功。
(2)allOf&anyOf
這兩個(gè)方法的入?yún)⑹且粋€(gè)completableFuture組、allOf就是所有任務(wù)都完成時(shí)返回,但是是個(gè)Void的返回值。
anyOf是當(dāng)入?yún)⒌腸ompletableFuture組中有一個(gè)任務(wù)執(zhí)行完畢就返回,返回結(jié)果是第一個(gè)完成的任務(wù)的結(jié)果。
publicstaticvoidotherStaticMethod()throwsExecutionException,InterruptedException{ finalCompletableFuturefutureOne=CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(3000); }catch(InterruptedExceptione){ System.out.println("futureOneInterruptedException"); } return"futureOneResult"; }); finalCompletableFuture futureTwo=CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(6000); }catch(InterruptedExceptione){ System.out.println("futureTwoInterruptedException"); } return"futureTwoResult"; }); CompletableFuturefuture=CompletableFuture.allOf(futureOne,futureTwo); System.out.println(future.get()); //CompletableFuturecompletableFuture=CompletableFuture.anyOf(futureOne,futureTwo); //System.out.println(completableFuture.get()); }
(3)completedFuture這個(gè)方法我沒懂他是干啥的,源碼就是返回一個(gè)值。感覺沒啥意義。
(4)取值方法,除了get還有一個(gè)getNow(); 這個(gè)就比較特殊了。
這個(gè)方法是執(zhí)行這個(gè)方法的時(shí)候任務(wù)執(zhí)行完了就返回任務(wù)的結(jié)果,如果任務(wù)沒有執(zhí)行完就返回你的入?yún)ⅰ?/p>
(5)join方法跟線程的join用法差不多。
(6)whenXXX,在一個(gè)任務(wù)執(zhí)行完成之后調(diào)用的方法。
這個(gè)有三個(gè)名差不多的方法:whenComplete、whenCompleteAsync、還有一個(gè)是whenCompleteAsync用自定義Executor
首先看一下這個(gè)whenComplete實(shí)例方法。這個(gè)就是任務(wù)執(zhí)行完畢調(diào)用的,傳入一個(gè)action,這個(gè)方法的執(zhí)行線程是當(dāng)前線程,意味著會(huì)阻塞當(dāng)前線程。
下面圖中test的輸出跟whenComplete方法運(yùn)行的線程有關(guān),運(yùn)行到main線程就會(huì)阻塞test的輸出,運(yùn)行的是completableFuture線程則不會(huì)阻塞住test的輸出。
下面是任務(wù)執(zhí)行的線程的探索。
根據(jù)測(cè)試得出的結(jié)論是:如果調(diào)用whenComplete的中途,還發(fā)生了其他事情,圖中的主線程的sleep(400);導(dǎo)致completableFuture這個(gè)任務(wù)執(zhí)行完畢了,那么就使用主線程調(diào)用。
如果調(diào)用的中途沒有發(fā)生其他任務(wù)且在觸碰到whenComplete方法時(shí)completableFuture這個(gè)任務(wù)還沒有徹底執(zhí)行完畢那么就會(huì)用completableFuture這個(gè)任務(wù)所使用的線程。
下面是whenCompleteAsync方法。這個(gè)方法就是新創(chuàng)建一個(gè)異步線程執(zhí)行。所以不會(huì)阻塞。
(7) then方法瞅著挺多的,實(shí)際上就是異不異步和加不加自定義Executor
注:whenComplete中出現(xiàn)的問題在then中測(cè)試不存在、使用的就是上一個(gè)任務(wù)的線程。這個(gè)thenCompose就是一個(gè)任務(wù)執(zhí)行完之后可以用它的返回結(jié)果接著執(zhí)行的方法,方法返回的是另一個(gè)你期盼泛型的結(jié)果。
compose理解就是上一個(gè)任務(wù)結(jié)果是then的一部分。
下面介紹一下thenCombine
這個(gè)combine的理解就是結(jié)合兩個(gè)任務(wù)的結(jié)果。
綜上:這個(gè)線程的問題并不是大問題,只要你不用線程來做判斷條件,他并不會(huì)影響你的效率。試想pool線程都執(zhí)行完了就用主線程跑唄。沒跑完,而使你等了那你就用pool線程唄。
thenRun就是這個(gè)任務(wù)運(yùn)行完,再運(yùn)行下一個(gè)任務(wù),感覺像是join了一下。
其余不再介紹,大同小異。
像thenApply(Function);這樣的就是有入?yún)⒂蟹祷刂殿愋偷摹?/p>
像thenAccept(Consumer);這樣的就是有入?yún)?,但是沒有返回值的。詳情在上文中有過關(guān)于函數(shù)式接口的敘述。
-
接口
+關(guān)注
關(guān)注
33文章
9005瀏覽量
153769 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4381瀏覽量
64898 -
JDK
+關(guān)注
關(guān)注
0文章
83瀏覽量
16893
原文標(biāo)題:Java 8 的異步利器:CompletableFuture源碼級(jí)解析(建議精讀)
文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
JDK動(dòng)態(tài)代理的原理
JDK的安裝、環(huán)境配置及使用
2017威哥Java教程視頻全集——從入門到精通(基礎(chǔ)課程+項(xiàng)目實(shí)戰(zhàn))
Java 那些最常用的工具類庫
JDK 15安裝步驟及新特性
java springboot電影購票選座微信小程序源碼功能簡(jiǎn)介
搭建基于HAL庫平臺(tái)的方法分享
java jdk6.0官方下載

Java開發(fā)工具包JDK1.8D安裝說明書

HashMap奪命14問,你能堅(jiān)持到第幾問?
基于JDK 1.8來分析Thread類的源碼
JDK中java.util.HashSet 類的介紹

JDK中常見的Lamada表達(dá)式

JDK中java.lang.Arrays 類的源碼解析

評(píng)論