0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

JDK1.8使用的接口類

Android編程精選 ? 來源:Java知音 ? 作者:Java知音 ? 2022-07-26 11:09 ? 次閱讀

completableFuture是JDK1.8版本新引入的類。下面是這個類:

38111d08-0c0f-11ed-ba43-dac502259ad0.png

實現了倆接口,本身是個class。這個是Future的實現類,使用completionStage接口去支持完成時觸發(fā)的函數和操作。

一個completetableFuture就代表了一個任務,他能用Future的方法,還能做一些之前說的executorService配合futures做不了的。

之前future需要等待isDone為true才能知道任務跑完了,或者就是用get方法調用的時候會出現阻塞,而使用completableFuture的使用就可以用then,when等等操作來防止以上的阻塞和輪詢isDone的現象出現。

1.創(chuàng)建CompletableFuture直接new對象。

一個completableFuture對象代表著一個任務,這個對象能跟這個任務產生聯系。

下面用的complete方法意思就是這個任務完成了需要返回的結果,然后用get()方法可以獲取到。

38221c20-0c0f-11ed-ba43-dac502259ad0.png

2.JDK1.8使用的接口類。

在本文的CompletableFuture中大量的使用了這些函數式接口。

注:這些聲明大量應用于方法的入參中,像thenApply和thenAccept這倆就是一個用Function一個用Consumer

而lambda函數正好是可以作為這些接口的實現。例如 s->{return 1;} 這個就相當于一個Function。因為有入參和返回結果。

3832c4d0-0c0f-11ed-ba43-dac502259ad0.png(1)Function

383e5a5c-0c0f-11ed-ba43-dac502259ad0.png(2)Consumer

384e7982-0c0f-11ed-ba43-dac502259ad0.png對于前面有Bi的就是這樣的,BiConsumer就是兩個參數的。

385bbea8-0c0f-11ed-ba43-dac502259ad0.png(3)Predicate這個接口聲明是一個入參,返回一個boolean。

38677144-0c0f-11ed-ba43-dac502259ad0.png(4)supplier

38776fcc-0c0f-11ed-ba43-dac502259ad0.png

3.下面是這個類的靜態(tài)方法

帶有Async就是異步執(zhí)行的意思、也是一個completableFuture對象代表著一個任務這個原則。

這種異步方法都可以指定一個線程池作為任務的運行環(huán)境,如果沒有指定就會使用ForkJoinPool線程池來執(zhí)行

3885b528-0c0f-11ed-ba43-dac502259ad0.png(1)supplyAsync&runAsync的使用例子。

publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{
ExecutorServiceexecutorService=Executors.newCachedThreadPool();
executorService.submit(newCallable(){
@Override
publicObjectcall()throwsException{
System.out.println("executorService是否為守護線程:"+Thread.currentThread().isDaemon());
returnnull;
}
});
finalCompletableFuturecompletableFuture=CompletableFuture.supplyAsync(()->{
System.out.println("thisislambdasupplyAsync");
System.out.println("supplyAsync是否為守護線程"+Thread.currentThread().isDaemon());
try{
TimeUnit.SECONDS.sleep(2);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("thislambdaisexecutedbyforkJoinPool");
return"result1";
});
finalCompletableFuturefuture=CompletableFuture.supplyAsync(()->{
System.out.println("thisistaskwithexecutor");
System.out.println("supplyAsync使用executorService時是否為守護線程:"+Thread.currentThread().isDaemon());
return"result2";
},executorService);
System.out.println(completableFuture.get());
System.out.println(future.get());
executorService.shutdown();
}

389f349e-0c0f-11ed-ba43-dac502259ad0.png這些任務中帶有supply是持有返回值的,run是void返回值的,在玩supply時發(fā)現一個問題:如果使用supplyAsync任務時不使用任務的返回值,即不用get方法阻塞主線程會導致任務執(zhí)行中斷。

注:跟get方法無關,后面有答案

38b40db0-0c0f-11ed-ba43-dac502259ad0.png38c84e88-0c0f-11ed-ba43-dac502259ad0.png然后我開始探索是否是只有supplyAsync是這樣。我測試了runAsync發(fā)現也是這樣。

38d6c0b2-0c0f-11ed-ba43-dac502259ad0.png下圖為與supplyAsync任務執(zhí)行不全面一樣的問題,我甚至測試了將lambda換成runnable發(fā)現無濟于事。

38f16818-0c0f-11ed-ba43-dac502259ad0.png

答案:

造成這個原因是因為Daemon。因為completableFuture這套使用異步任務的操作都是創(chuàng)建成了守護線程,那么我們沒有調用get方法不阻塞這個主線程的時候。主線程執(zhí)行完畢,所有線程執(zhí)行完畢就會導致一個問題,就是守護線程退出。

那么我們沒有執(zhí)行的代碼就是因為主線程不再跑任務而關閉導致的,可能這個不叫問題,因為在開發(fā)中我們主線程常常是一直開著的。但是這個小問題同樣讓我想了好久。

下面我們開一個非守護線程,可以看到程序執(zhí)行順利。

3900667e-0c0f-11ed-ba43-dac502259ad0.png下面證實守護線程在其他非守護線程全部退出的情況下不繼續(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";
});
finalCompletableFuturefutureTwo=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());
}

3911a466-0c0f-11ed-ba43-dac502259ad0.png3921c3e6-0c0f-11ed-ba43-dac502259ad0.png(3)completedFuture這個方法我沒懂他是干啥的,源碼就是返回一個值。感覺沒啥意義。

392f1a3c-0c0f-11ed-ba43-dac502259ad0.png(4)取值方法,除了get還有一個getNow(); 這個就比較特殊了。

這個方法是執(zhí)行這個方法的時候任務執(zhí)行完了就返回任務的結果,如果任務沒有執(zhí)行完就返回你的入參。

393ba2d4-0c0f-11ed-ba43-dac502259ad0.png(5)join方法跟線程的join用法差不多。

394cbff6-0c0f-11ed-ba43-dac502259ad0.png(6)whenXXX,在一個任務執(zhí)行完成之后調用的方法。

這個有三個名差不多的方法:whenComplete、whenCompleteAsync、還有一個是whenCompleteAsync用自定義Executor

395996a4-0c0f-11ed-ba43-dac502259ad0.png首先看一下這個whenComplete實例方法。這個就是任務執(zhí)行完畢調用的,傳入一個action,這個方法的執(zhí)行線程是當前線程,意味著會阻塞當前線程。

下面圖中test的輸出跟whenComplete方法運行的線程有關,運行到main線程就會阻塞test的輸出,運行的是completableFuture線程則不會阻塞住test的輸出。

396f87e8-0c0f-11ed-ba43-dac502259ad0.png下面是任務執(zhí)行的線程的探索。

397f5538-0c0f-11ed-ba43-dac502259ad0.png398f4f7e-0c0f-11ed-ba43-dac502259ad0.png根據測試得出的結論是:如果調用whenComplete的中途,還發(fā)生了其他事情,圖中的主線程的sleep(400);導致completableFuture這個任務執(zhí)行完畢了,那么就使用主線程調用。

如果調用的中途沒有發(fā)生其他任務且在觸碰到whenComplete方法時completableFuture這個任務還沒有徹底執(zhí)行完畢那么就會用completableFuture這個任務所使用的線程。

下面是whenCompleteAsync方法。這個方法就是新創(chuàng)建一個異步線程執(zhí)行。所以不會阻塞。

39a35bae-0c0f-11ed-ba43-dac502259ad0.png(7) then方法瞅著挺多的,實際上就是異不異步和加不加自定義Executor

39b0ea4e-0c0f-11ed-ba43-dac502259ad0.png

注:whenComplete中出現的問題在then中測試不存在、使用的就是上一個任務的線程。這個thenCompose就是一個任務執(zhí)行完之后可以用它的返回結果接著執(zhí)行的方法,方法返回的是另一個你期盼泛型的結果。

compose理解就是上一個任務結果是then的一部分。

39c652da-0c0f-11ed-ba43-dac502259ad0.png下面介紹一下thenCombine

這個combine的理解就是結合兩個任務的結果。

39d76f70-0c0f-11ed-ba43-dac502259ad0.png綜上:這個線程的問題并不是大問題,只要你不用線程來做判斷條件,他并不會影響你的效率。試想pool線程都執(zhí)行完了就用主線程跑唄。沒跑完,而使你等了那你就用pool線程唄。

thenRun就是這個任務運行完,再運行下一個任務,感覺像是join了一下。

39eb6138-0c0f-11ed-ba43-dac502259ad0.png其余不再介紹,大同小異。

像thenApply(Function);這樣的就是有入參有返回值類型的。

像thenAccept(Consumer);這樣的就是有入參,但是沒有返回值的。詳情在上文中有過關于函數式接口的敘述。

審核編輯:彭靜

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8254

    瀏覽量

    149943
  • 函數
    +關注

    關注

    3

    文章

    4235

    瀏覽量

    61965
  • JDK
    JDK
    +關注

    關注

    0

    文章

    80

    瀏覽量

    16548

原文標題:Java 8 的異步利器:CompletableFuture源碼級解析(建議精讀)

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    JDK動態(tài)代理的原理

    在Java中,動態(tài)代理是一種機制,允許在運行時動態(tài)地創(chuàng)建代理對象來代替某個實際對象,從而在其前后執(zhí)行額外的邏輯。 為什么JDK動態(tài)代理只能代理接口實現,原因是JDK動態(tài)代理是基于
    的頭像 發(fā)表于 09-30 10:51 ?466次閱讀

    JDK的安裝、環(huán)境配置及使用

    ,變量值中輸入庫名C:\jdk1.5\lib\dt.jar;C:\jdk1.5\lib\tools.jar;然后選擇確定。再次選擇“系統(tǒng)變量”的“新建”,在變量名中輸入path,變量值中輸入C
    發(fā)表于 12-06 00:19

    2017威哥Java教程視頻全集——從入門到精通(基礎課程+項目實戰(zhàn))

    ` 本帖最后由 lzr858585 于 2017-9-6 17:04 編輯 課程介紹:本套 Java課程基于最新的 JDK1.8版本,加入更多新特性講解,盡力打造超全面、極細致、更深入的特點,從
    發(fā)表于 09-06 17:02

    Java 那些最常用的工具

    :https://blog.csdn.net/zzti_erlie/article/details/100849192兩者的api很相似,如果公司的jdk版本在1.8以上推薦使用jdk1.8新推出的日期
    發(fā)表于 06-15 17:18

    JDK 15安裝步驟及新特性

    封閉(預覽特性)  可以是封閉和或者封閉接口,用來增強 Java 編程語言,防止其他接口擴展或實現它們?! ∮辛诉@個特性,意味著以
    發(fā)表于 12-23 17:36

    java springboot電影購票選座微信小程序源碼功能簡介

    功能簡介后臺:會員管理,電影管理,訂單管理,系統(tǒng)管理小程序端:首頁電影 選座 影院 我的 訂單環(huán)境準備jdk1.8 mysql5.7 eclipse(idea) navicat后臺框架springboot mybatis vue.js bootstrap具體實踐...
    發(fā)表于 12-30 06:15

    搭建基于HAL庫平臺的方法分享

    的安裝基于HAL庫進行STM32開發(fā),由于STM32CubeMX軟件是基于Java環(huán)境運行的,因此需要安裝JRE,可以再JAVA官網上下載安裝文件。大家根據自己的操作系統(tǒng)安裝合適的版本?。ū救艘恢笔褂玫氖?b class='flag-5'>JDK1.8)如果平時有使用Java編程的,可以直接下載JDK,它會
    發(fā)表于 02-28 12:45

    java jdk6.0官方下載

    java jdk6.0下載如何件: java jdk6.0安裝步驟: 第一步 JDK1.6的安裝步驟 第一步雙擊安裝文件jdk-6u7-windows-i586-p.exe
    發(fā)表于 10-17 11:47 ?155次下載
    java <b class='flag-5'>jdk</b>6.0官方下載

    虛擬機:CentOS 7通過yum安裝JDK1.8的方法

    虛擬機:CentOS 7通過yum安裝JDK1.8的方法
    的頭像 發(fā)表于 07-02 18:02 ?3102次閱讀

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

    本文檔的主要內容介紹的是Java開發(fā)工具包JDK1.8D安裝說明書資料免費下載。
    發(fā)表于 07-16 08:00 ?33次下載
    Java開發(fā)工具包<b class='flag-5'>JDK1.8</b>D安裝說明書

    HashMap奪命14問,你能堅持到第幾問?

    JDK1.8中,有“數組+鏈表+紅黑樹”組成。當鏈表過長,則會嚴重影響HashMap的性能,紅黑樹搜索時間復雜度是O(logn),而鏈表是O(n)。因此,JDK1.8對數據結構做了進一步的優(yōu)化,引入了紅黑樹,鏈表和紅黑樹在達到一定條件會進行轉換:
    的頭像 發(fā)表于 04-13 14:40 ?737次閱讀

    基于JDK 1.8來分析Thread的源碼

    由上圖我們可以看出,Thread實現了Runnable接口,而Runnable在JDK 1.8中被@FunctionalInterface注解標記為函數式
    的頭像 發(fā)表于 02-06 17:12 ?530次閱讀

    JDK中java.util.HashSet 的介紹

    JDK1.8 中,HashMap 是由 數組+鏈表+紅黑樹構成,相對于早期版本的 JDK HashMap 實現,新增了紅黑樹作為底層數據結構,在數據量較大且哈希碰撞較多時,能夠極大的增加檢索
    的頭像 發(fā)表于 10-09 10:50 ?474次閱讀
    <b class='flag-5'>JDK</b>中java.util.HashSet <b class='flag-5'>類</b>的介紹

    JDK中常見的Lamada表達式

    JDK中有許多函數式接口,也會有許多方法會使用函數式接口作為參數,同時在各種源碼中也大量使用了這些方法,那么我們在實際工作中應該如何使用!我們就來盤一盤,這樣也有助于寫出優(yōu)雅的代碼,使我們在閱讀源碼
    的頭像 發(fā)表于 10-10 15:07 ?422次閱讀
    <b class='flag-5'>JDK</b>中常見的Lamada表達式

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

    日常開發(fā)中,我們會使用各種工具,利用封裝好的輪子,能讓我們的開發(fā)事半功倍。但是在JDK中,有一個特別的工具——java.lang.Arrays.class,其源碼實現還是挺精湛,接下來讓我們來
    的頭像 發(fā)表于 10-11 15:31 ?497次閱讀
    <b class='flag-5'>JDK</b>中java.lang.Arrays <b class='flag-5'>類</b>的源碼解析