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

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

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

JDK中常見的Lamada表達式

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

評論