欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的CompletableFuture基本用法

 更新時(shí)間:2024年01月22日 10:56:15   作者:tong_master  
這篇文章主要介紹了Java中的CompletableFuture基本用法,CompletableFuture是java.util.concurrent庫(kù)在java 8中新增的主要工具,同傳統(tǒng)的Future相比,其支持流式計(jì)算、函數(shù)式編程、完成通知、自定義異常處理等很多新的特性,需要的朋友可以參考下

前言

CompletableFuture是java.util.concurrent庫(kù)在java 8中新增的主要工具,同傳統(tǒng)的Future相比,其支持流式計(jì)算、函數(shù)式編程、完成通知、自定義異常處理等很多新的特性。

由于函數(shù)式編程在java中越來(lái)越多的被使用到,熟練掌握CompletableFuture,對(duì)于更好的使用java 8后的主要新特性很重要。

簡(jiǎn)單起見(jiàn),本文使用的CompletableFuture版本為java 8(java 11的CompletableFuture新增了一些方法)。

1、為什么叫CompletableFuture?

CompletableFuture字面翻譯過(guò)來(lái),就是“可完成的Future”。

同傳統(tǒng)的Future相比較,CompletableFuture能夠主動(dòng)設(shè)置計(jì)算的結(jié)果值(主動(dòng)終結(jié)計(jì)算過(guò)程,即completable),從而在某些場(chǎng)景下主動(dòng)結(jié)束阻塞等待。

而Future由于不能主動(dòng)設(shè)置計(jì)算結(jié)果值,一旦調(diào)用get()進(jìn)行阻塞等待,要么當(dāng)計(jì)算結(jié)果產(chǎn)生,要么超時(shí),才會(huì)返回。

下面的示例,比較簡(jiǎn)單的說(shuō)明了,CompletableFuture是如何被主動(dòng)完成的。

在下面這段代碼中,由于調(diào)用了complete方法,所以最終的打印結(jié)果是“manual test”,而不是"test"。

CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
    try{
        Thread.sleep(1000L);
        return "test";
    } catch (Exception e){
        return "failed test";
    }
});
future.complete("manual test");
System.out.println(future.join());

2、創(chuàng)建CompletableFuture

2.1 構(gòu)造函數(shù)創(chuàng)建

最簡(jiǎn)單的方式就是通過(guò)構(gòu)造函數(shù)創(chuàng)建一個(gè)CompletableFuture實(shí)例。如下代碼所示。由于新創(chuàng)建的CompletableFuture還沒(méi)有任何計(jì)算結(jié)果,這時(shí)調(diào)用join,當(dāng)前線程會(huì)一直阻塞在這里。

CompletableFuture<String> future = new CompletableFuture();
String result = future.join();
System.out.println(result);

此時(shí),如果在另外一個(gè)線程中,主動(dòng)設(shè)置該CompletableFuture的值,則上面線程中的結(jié)果就能返回。

future.complete("test");

這展示了CompletableFuture最簡(jiǎn)單的創(chuàng)建及使用方法。

2.2 supplyAsync創(chuàng)建

CompletableFuture.supplyAsync()也可以用來(lái)創(chuàng)建CompletableFuture實(shí)例。通過(guò)該函數(shù)創(chuàng)建的CompletableFuture實(shí)例會(huì)異步執(zhí)行當(dāng)前傳入的計(jì)算任務(wù)。在調(diào)用端,則可以通過(guò)get或join獲取最終計(jì)算結(jié)果。

supplyAsync有兩種簽名:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

第一種只需傳入一個(gè)Supplier實(shí)例(一般使用lamda表達(dá)式),此時(shí)框架會(huì)默認(rèn)使用ForkJoin的線程池來(lái)執(zhí)行被提交的任務(wù)。

第二種可以指定自定義的線程池,然后將任務(wù)提交給該線程池執(zhí)行。

下面為使用supplyAsync創(chuàng)建CompletableFuture的示例:

CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
    System.out.println("compute test");
    return "test";
});
String result = future.join();
System.out.println("get result: " + result);

在示例中,異步任務(wù)中會(huì)打印出“compute test”,并返回"test"作為最終計(jì)算結(jié)果。所以,最終的打印信息為“get result: test”。

2.3 runAsync創(chuàng)建

CompletableFuture.runAsync()也可以用來(lái)創(chuàng)建CompletableFuture實(shí)例。與supplyAsync()不同的是,runAsync()傳入的任務(wù)要求是Runnable類(lèi)型的,所以沒(méi)有返回值。因此,runAsync適合創(chuàng)建不需要返回值的計(jì)算任務(wù)。同supplyAsync()類(lèi)似,runAsync()也有兩種簽名:

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

 下面為使用runAsync()的例子:

CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
    System.out.println("compute test");
});
System.out.println("get result: " + future.join());

在示例中,由于任務(wù)沒(méi)有返回值, 所以最后的打印結(jié)果是"get result: null"。

3、常見(jiàn)的使用方式

同F(xiàn)uture相比,CompletableFuture最大的不同是支持流式(Stream)的計(jì)算處理,多個(gè)任務(wù)之間,可以前后相連,從而形成一個(gè)計(jì)算流。比如:任務(wù)1產(chǎn)生的結(jié)果,可以直接作為任務(wù)2的入?yún)?,參與任務(wù)2的計(jì)算,以此類(lèi)推。

CompletableFuture中常用的流式連接函數(shù)包括:

  • thenApply
  • thenApplyAsync
  • thenAccept
  • thenAcceptAsync
  • thenRun
  • thenRunAsync
  • thenCombine
  • thenCombineAsync
  • thenCompose
  • thenComposeAsync
  • whenComplete
  • whenCompleteAsync
  • handle
  • handleAsync

其中,帶Async后綴的函數(shù)表示需要連接的后置任務(wù)會(huì)被單獨(dú)提交到線程池中,從而相對(duì)前置任務(wù)來(lái)說(shuō)是異步運(yùn)行的。

除此之外,兩者沒(méi)有其他區(qū)別。

因此,為了快速理解,在接下來(lái)的介紹中,我們主要介紹不帶Async的版本。

3.1 thenApply / thenAccept / thenRun

這里將thenApply / thenAccept / thenRun放在一起講,因?yàn)檫@幾個(gè)連接函數(shù)之間的唯一區(qū)別是提交的任務(wù)類(lèi)型不一樣。

  • thenApply提交的任務(wù)類(lèi)型需遵從Function簽名,也就是有入?yún)⒑头祷刂担渲腥雲(yún)榍爸萌蝿?wù)的結(jié)果。
  • thenAccept提交的任務(wù)類(lèi)型需遵從Consumer簽名,也就是有入?yún)⒌菦](méi)有返回值,其中入?yún)榍爸萌蝿?wù)的結(jié)果。
  • thenRun提交的任務(wù)類(lèi)型需遵從Runnable簽名,即沒(méi)有入?yún)⒁矝](méi)有返回值。

因此,簡(jiǎn)單起見(jiàn),我們這里主要講thenApply。

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 1");
    return 1;
});
CompletableFuture<Integer> future2 = future1.thenApply((p)->{
    System.out.println("compute 2");
    return p+10;
});
System.out.println("result: " + future2.join());

在上面的示例中,future1通過(guò)調(diào)用thenApply將后置任務(wù)連接起來(lái),并形成future2。

該示例的最終打印結(jié)果為11,可見(jiàn)程序在運(yùn)行中,future1的結(jié)果計(jì)算出來(lái)后,會(huì)傳遞給通過(guò)thenApply連接的任務(wù),從而產(chǎn)生future2的最終結(jié)果為1+10=11。

當(dāng)然,在實(shí)際使用中,我們理論上可以無(wú)限連接后續(xù)計(jì)算任務(wù),從而實(shí)現(xiàn)鏈條更長(zhǎng)的流式計(jì)算。

需要注意的是,通過(guò)thenApply / thenAccept / thenRun連接的任務(wù),當(dāng)且僅當(dāng)前置任務(wù)計(jì)算完成時(shí),才會(huì)開(kāi)始后置任務(wù)的計(jì)算。因此,這組函數(shù)主要用于連接前后有依賴的任務(wù)鏈。

3.2 thenCombine

同前面一組連接函數(shù)相比,thenCombine最大的不同是連接任務(wù)可以是一個(gè)獨(dú)立的CompletableFuture(或者是任意實(shí)現(xiàn)了CompletionStage的類(lèi)型),從而允許前后連接的兩個(gè)任務(wù)可以并行執(zhí)行(后置任務(wù)不需要等待前置任務(wù)執(zhí)行完成),最后當(dāng)兩個(gè)任務(wù)均完成時(shí),再將其結(jié)果同時(shí)傳遞給下游處理任務(wù),從而得到最終結(jié)果。

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
            System.out.println("compute 1");
            return 1;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(()->{
            System.out.println("compute 2");
            return 10;
        });
        CompletableFuture<Integer> future3 = future1.thenCombine(future2, (r1, r2)->r1 + r2);
        System.out.println("result: " + future3.join());

上面示例代碼中,future1和future2為獨(dú)立的CompletableFuture任務(wù),他們分別會(huì)在各自的線程中并行執(zhí)行,然后future1通過(guò)thenCombine與future2連接,并且以lamda表達(dá)式傳入處理結(jié)果的表達(dá)式,該表達(dá)式代表的任務(wù)會(huì)將future1與future2的結(jié)果作為入?yún)⒉⒂?jì)算他們的和。

因此,上面示例代碼中,最終的打印結(jié)果是11。

一般,在連接任務(wù)之間互相不依賴的情況下,可以使用thenCombine來(lái)連接任務(wù),從而提升任務(wù)之間的并發(fā)度。

注意,thenAcceptBoth、thenAcceptBothAsync、runAfterBoth、runAfterBothAsync的作用與thenConbime類(lèi)似,唯一不同的地方是任務(wù)類(lèi)型不同,分別是BiConumser、Runnable。

3.3 thenCompose

前面講了thenCombine主要用于沒(méi)有前后依賴關(guān)系之間的任務(wù)進(jìn)行連接。那么,如果兩個(gè)任務(wù)之間有前后依賴關(guān)系,但是連接任務(wù)又是獨(dú)立的CompletableFuture,該怎么實(shí)現(xiàn)呢?

先來(lái)看一下直接使用thenApply來(lái)實(shí)現(xiàn):

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 1");
    return 1;
});
CompletableFuture<CompletableFuture<Integer>> future2 =
        future1.thenApply((r)->CompletableFuture.supplyAsync(()->r+10));
System.out.println(future2.join().join());

可以發(fā)現(xiàn),上面示例代碼中,future2的類(lèi)型變成了CompletableFuture嵌套,而且在獲取結(jié)果的時(shí)候,也需要嵌套調(diào)用join或者get。

這樣,當(dāng)連接的任務(wù)越多時(shí),代碼會(huì)變得越來(lái)越復(fù)雜,嵌套獲取層級(jí)也越來(lái)越深。

因此,需要一種方式,能將這種嵌套模式展開(kāi),使其沒(méi)有那么多層級(jí)。

thenCompose的主要目的就是解決這個(gè)問(wèn)題(這里也可以將thenCompose的作用類(lèi)比于stream接口中的flatMap,因?yàn)樗麄兌伎梢詫㈩?lèi)型嵌套展開(kāi))。

看一下通過(guò)thenCompose如何實(shí)現(xiàn)上面的代碼:

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
            System.out.println("compute 1");
            return 1;
        });
        CompletableFuture<Integer> future2 = future1.thenCompose((r)->CompletableFuture.supplyAsync(()->r+10));
        System.out.println(future2.join());

通過(guò)示例代碼可以看出來(lái),很明顯,在使用了thenCompose后,future2不再存在CompletableFuture類(lèi)型嵌套了,從而比較簡(jiǎn)潔的達(dá)到了我們的目的。

3.4 whenComplete

whenComplete主要用于注入任務(wù)完成時(shí)的回調(diào)通知邏輯。這個(gè)解決了傳統(tǒng)future在任務(wù)完成時(shí),無(wú)法主動(dòng)發(fā)起通知的問(wèn)題。前置任務(wù)會(huì)將計(jì)算結(jié)果或者拋出的異常作為入?yún)鬟f給回調(diào)通知函數(shù)。

以下為示例:

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 1");
    return 1;
});
CompletableFuture future2 = future1.whenComplete((r, e)->{
    if(e != null){
        System.out.println("compute failed!");
    } else {
        System.out.println("received result is " + r);
    }
});
System.out.println("result: " + future2.join());

需要注意的是,future2獲得的結(jié)果是前置任務(wù)的結(jié)果,whenComplete中的邏輯不會(huì)影響計(jì)算結(jié)果。

3.5 handle

handle與whenComplete的作用有些類(lèi)似,但是handle接收的處理函數(shù)有返回值,而且返回值會(huì)影響最終獲取的計(jì)算結(jié)果。

以下為示例:

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("compute 1");
    return 1;
});
CompletableFuture<Integer> future2 = future1.handle((r, e)->{
    if(e != null){
        System.out.println("compute failed!");
        return r;
    } else {
        System.out.println("received result is " + r);
        return r + 10;
    }
});
System.out.println("result: " + future2.join());

在以上示例中,打印出的最終結(jié)果為11。說(shuō)明經(jīng)過(guò)handle計(jì)算后產(chǎn)生了新的結(jié)果。

到此這篇關(guān)于Java中的CompletableFuture基本用法的文章就介紹到這了,更多相關(guān)CompletableFuture用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Springboot集成SSE實(shí)現(xiàn)單工通信消息推送流程詳解

    Springboot集成SSE實(shí)現(xiàn)單工通信消息推送流程詳解

    SSE簡(jiǎn)單的來(lái)說(shuō)就是服務(wù)器主動(dòng)向前端推送數(shù)據(jù)的一種技術(shù),它是單向的,也就是說(shuō)前端是不能向服務(wù)器發(fā)送數(shù)據(jù)的。SSE適用于消息推送,監(jiān)控等只需要服務(wù)器推送數(shù)據(jù)的場(chǎng)景中,下面是使用Spring Boot來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模擬向前端推動(dòng)進(jìn)度數(shù)據(jù),前端頁(yè)面接受后展示進(jìn)度條
    2022-11-11
  • Spring中的@ConditionalOnProperty注解使用詳解

    Spring中的@ConditionalOnProperty注解使用詳解

    這篇文章主要介紹了Spring中的@ConditionalOnProperty注解使用詳解,在 spring boot 中有時(shí)候需要控制配置類(lèi)是否生效,可以使用 @ConditionalOnProperty 注解來(lái)控制 @Configuration 是否生效,需要的朋友可以參考下
    2024-01-01
  • 淺析RxJava處理復(fù)雜表單驗(yàn)證問(wèn)題的方法

    淺析RxJava處理復(fù)雜表單驗(yàn)證問(wèn)題的方法

    這篇文章主要介紹了RxJava處理復(fù)雜表單驗(yàn)證問(wèn)題的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • java 排序算法之冒泡排序

    java 排序算法之冒泡排序

    這篇文章主要介紹了java 排序算法之冒泡排序,文中運(yùn)用大量的代碼講解相關(guān)知識(shí),非常詳細(xì),感興趣的小伙伴可以參考一下
    2021-09-09
  • 解決springboot+shiro 權(quán)限攔截失效的問(wèn)題

    解決springboot+shiro 權(quán)限攔截失效的問(wèn)題

    這篇文章主要介紹了解決springboot+shiro 權(quán)限攔截失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java高級(jí)應(yīng)用之斗地主游戲

    Java高級(jí)應(yīng)用之斗地主游戲

    這篇文章主要為大家詳細(xì)介紹了Java高級(jí)應(yīng)用之斗地主游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Java中g(shù)et/post的https請(qǐng)求忽略ssl證書(shū)認(rèn)證淺析

    Java中g(shù)et/post的https請(qǐng)求忽略ssl證書(shū)認(rèn)證淺析

    因?yàn)镴ava在安裝的時(shí)候,會(huì)默認(rèn)導(dǎo)入某些根證書(shū),所以有些網(wǎng)站不導(dǎo)入證書(shū),也可以使用Java進(jìn)行訪問(wèn),這篇文章主要給大家介紹了關(guān)于Java中g(shù)et/post的https請(qǐng)求忽略ssl證書(shū)認(rèn)證的相關(guān)資料,需要的朋友可以參考下
    2024-01-01
  • Eclipse中引入com.sun.image.codec.jpeg包報(bào)錯(cuò)的完美解決辦法

    Eclipse中引入com.sun.image.codec.jpeg包報(bào)錯(cuò)的完美解決辦法

    Java開(kāi)發(fā)中對(duì)圖片的操作需要引入 com.sun.image.codec.jpeg,但有時(shí)引入這個(gè)包會(huì)報(bào)錯(cuò),利用下面的操作可以完成解決這個(gè)問(wèn)題
    2018-02-02
  • SpringBoot+ThreadLocal+AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源

    SpringBoot+ThreadLocal+AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源

    最近在做業(yè)務(wù)需求時(shí),需要從不同的數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)然后寫(xiě)入到當(dāng)前數(shù)據(jù)庫(kù)中,因此涉及到切換數(shù)據(jù)源問(wèn)題,所以本文采用ThreadLocal+AbstractRoutingDataSource來(lái)模擬實(shí)現(xiàn)dynamic-datasource-spring-boot-starter中線程數(shù)據(jù)源切換,需要的朋友可以參考下
    2023-08-08
  • Jmeter中的timeshift()函數(shù)獲取當(dāng)前時(shí)間進(jìn)行加減

    Jmeter中的timeshift()函數(shù)獲取當(dāng)前時(shí)間進(jìn)行加減

    這篇文章主要介紹了Jmeter中的timeshift()函數(shù)獲取當(dāng)前時(shí)間進(jìn)行加減,TimeShift(格式,日期,移位,語(yǔ)言環(huán)境,變量)可對(duì)日期進(jìn)行移位加減操作,本文給大家詳細(xì)講解,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10

最新評(píng)論