completableFuture是JDK1.8版本新引入的類。下面是這個類:
實現(xiàn)了倆接口,本身是個class。這個是Future的實現(xiàn)類,使用completionStage接口去支持完成時觸發(fā)的函數(shù)和操作。
一個completetableFuture就代表了一個任務,他能用Future的方法,還能做一些之前說的executorService配合futures做不了的。
之前future需要等待isDone為true才能知道任務跑完了,或者就是用get方法調用的時候會出現(xiàn)阻塞,而使用completableFuture的使用就可以用then,when等等操作來防止以上的阻塞和輪詢isDone的現(xiàn)象出現(xiàn)。
1.創(chuàng)建CompletableFuture直接new對象。
一個completableFuture對象代表著一個任務,這個對象能跟這個任務產生聯(lián)系。
下面用的complete方法意思就是這個任務完成了需要返回的結果,然后用get()方法可以獲取到。
2.JDK1.8使用的接口類。
在本文的CompletableFuture中大量的使用了這些函數(shù)式接口。
注:這些聲明大量應用于方法的入參中,像thenApply和thenAccept這倆就是一個用Function一個用Consumer
而lambda函數(shù)正好是可以作為這些接口的實現(xiàn)。例如 s->{return 1;} 這個就相當于一個Function。因為有入參和返回結果。
(1)Function
(2)Consumer
對于前面有Bi的就是這樣的,BiConsumer就是兩個參數(shù)的。
(3)Predicate這個接口聲明是一個入參,返回一個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方法無關,后面有答案
然后我開始探索是否是只有supplyAsync是這樣。我測試了runAsync發(fā)現(xiàn)也是這樣。
下圖為與supplyAsync任務執(zhí)行不全面一樣的問題,我甚至測試了將lambda換成runnable發(fā)現(xiàn)無濟于事。
答案:
造成這個原因是因為Daemon。因為completableFuture這套使用異步任務的操作都是創(chuàng)建成了守護線程,那么我們沒有調用get方法不阻塞這個主線程的時候。主線程執(zhí)行完畢,所有線程執(zhí)行完畢就會導致一個問題,就是守護線程退出。
那么我們沒有執(zhí)行的代碼就是因為主線程不再跑任務而關閉導致的,可能這個不叫問題,因為在開發(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了一秒。其他線程就一句控制臺輸出的代碼,最終的結果是文件沒有任何變化。
當我把主線程sleep 5秒時,本地文件會寫入一句 this is completableFuture daemon test 驗證成功。
(2)allOf&anyOf
這兩個方法的入參是一個completableFuture組、allOf就是所有任務都完成時返回,但是是個Void的返回值。
anyOf是當入參的completableFuture組中有一個任務執(zhí)行完畢就返回,返回結果是第一個完成的任務的結果。
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í)行完了就返回任務的結果,如果任務沒有執(zhí)行完就返回你的入參。
(5)join方法跟線程的join用法差不多。
(6)whenXXX,在一個任務執(zhí)行完成之后調用的方法。
這個有三個名差不多的方法:whenComplete、whenCompleteAsync、還有一個是whenCompleteAsync用自定義Executor
首先看一下這個whenComplete實例方法。這個就是任務執(zhí)行完畢調用的,傳入一個action,這個方法的執(zhí)行線程是當前線程,意味著會阻塞當前線程。
下面圖中test的輸出跟whenComplete方法運行的線程有關,運行到main線程就會阻塞test的輸出,運行的是completableFuture線程則不會阻塞住test的輸出。
下面是任務執(zhí)行的線程的探索。
根據測試得出的結論是:如果調用whenComplete的中途,還發(fā)生了其他事情,圖中的主線程的sleep(400);導致completableFuture這個任務執(zhí)行完畢了,那么就使用主線程調用。
如果調用的中途沒有發(fā)生其他任務且在觸碰到whenComplete方法時completableFuture這個任務還沒有徹底執(zhí)行完畢那么就會用completableFuture這個任務所使用的線程。
下面是whenCompleteAsync方法。這個方法就是新創(chuàng)建一個異步線程執(zhí)行。所以不會阻塞。
(7) then方法瞅著挺多的,實際上就是異不異步和加不加自定義Executor
注:whenComplete中出現(xiàn)的問題在then中測試不存在、使用的就是上一個任務的線程。這個thenCompose就是一個任務執(zhí)行完之后可以用它的返回結果接著執(zhí)行的方法,方法返回的是另一個你期盼泛型的結果。
compose理解就是上一個任務結果是then的一部分。
下面介紹一下thenCombine
這個combine的理解就是結合兩個任務的結果。
綜上:這個線程的問題并不是大問題,只要你不用線程來做判斷條件,他并不會影響你的效率。試想pool線程都執(zhí)行完了就用主線程跑唄。沒跑完,而使你等了那你就用pool線程唄。
thenRun就是這個任務運行完,再運行下一個任務,感覺像是join了一下。
其余不再介紹,大同小異。
像thenApply(Function);這樣的就是有入參有返回值類型的。
像thenAccept(Consumer);這樣的就是有入參,但是沒有返回值的。詳情在上文中有過關于函數(shù)式接口的敘述。
-
接口
+關注
關注
33文章
8374瀏覽量
150570 -
函數(shù)
+關注
關注
3文章
4262瀏覽量
62243 -
JDK
+關注
關注
0文章
80瀏覽量
16566
原文標題:Java 8 的異步利器:CompletableFuture源碼級解析(建議精讀)
文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論