CompletableFuture牢屋、CompletionStage 和 Future 的對(duì)比

1. 共同點(diǎn)

  • 異步編程: 這三種機(jī)制都用于處理異步操作且预,即在不阻塞當(dāng)前線程的情況下執(zhí)行任務(wù),并在任務(wù)完成后通知結(jié)果烙无。

  • 結(jié)果獲确嫘场: 所有三種機(jī)制都提供獲取異步操作結(jié)果的方法,例如:

    • Future.get()
    • CompletionStage.toCompletableFuture().join()
    • CompletableFuture.join()
  • 錯(cuò)誤處理: 所有三種機(jī)制都提供處理異步操作中發(fā)生的異常的方法截酷,例如:

    • Future.get() 可能會(huì)拋出異常涮拗。
    • CompletionStage.exceptionally()CompletableFuture.exceptionally() 可以捕獲異常。

2. 優(yōu)勢(shì)

Future

  • 簡(jiǎn)單易用: Future 是 Java 1.5 中引入的迂苛,相對(duì)來(lái)說(shuō)比較簡(jiǎn)單三热,易于理解。
  • 基礎(chǔ)機(jī)制: Future 是異步操作的核心三幻,其他的機(jī)制就漾,例如 CompletionStageCompletableFuture 都是基于 Future 實(shí)現(xiàn)的。

CompletionStage

  • 靈活的組合和處理: CompletionStage 提供了豐富的組合方法念搬,例如 thenApply抑堡、thenAcceptthenCompose 等锁蠕,可以方便地將多個(gè)異步操作進(jìn)行組合和處理夷野,以滿足復(fù)雜業(yè)務(wù)需求。
  • 鏈?zhǔn)秸{(diào)用: CompletionStage 支持鏈?zhǔn)秸{(diào)用荣倾,可以將多個(gè)異步操作連接起來(lái)悯搔,形成一個(gè)完整的異步操作流程,簡(jiǎn)化代碼編寫(xiě)舌仍。

CompletableFuture

  • 功能更豐富: CompletableFuture 提供了更加豐富的方法妒貌,除了 CompletionStage 接口中定義的方法之外,還提供了 complete铸豁、completeExceptionally灌曙、join 等方法,可以更加靈活地控制異步操作节芥。
  • 高效: CompletableFuture 的實(shí)現(xiàn)更加高效在刺,因?yàn)樗褂昧艘恍﹥?yōu)化技術(shù)逆害,例如延遲初始化和緩存。

3. 示例代碼

3.1蚣驼、Future

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureExample {

    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(() -> {
            // 模擬耗時(shí)操作
            Thread.sleep(1000);
            return "Hello, Future!";
        });

        // 獲取結(jié)果
        String result = future.get();
        System.out.println("Result: " + result);

        executor.shutdown();
    }
}

3.2魄幕、CompletionStage

import java.util.concurrent.CompletableFuture;

public class CompletionStageExample {

    public static void main(String[] args) {
        CompletionStage<String> future = CompletableFuture.supplyAsync(() -> {
            // 模擬耗時(shí)操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello, CompletionStage!";
        });

        future.thenApply(s -> s.toUpperCase())
                .thenAccept(System.out::println)
                .exceptionally(throwable -> {
                    System.err.println("Error occurred: " + throwable.getMessage());
                    return null;
                });
    }
}

進(jìn)階用法

3.3、颖杏、將兩個(gè) CompletionStage 的結(jié)果合并在一起
我們以一個(gè)常見(jiàn)的場(chǎng)景 — 網(wǎng)絡(luò)請(qǐng)求為例纯陨,來(lái)說(shuō)明 CompletionStage 的鏈?zhǔn)秸{(diào)用如何簡(jiǎn)化代碼:
場(chǎng)景描述:
假設(shè)你需要從兩個(gè)不同的 API 獲取數(shù)據(jù),然后將這兩個(gè)數(shù)據(jù)進(jìn)行合并處理留储,最后將合并后的數(shù)據(jù)展示給用戶(hù)翼抠。
傳統(tǒng)方法:

// 使用傳統(tǒng)的回調(diào)方式
public void fetchAndProcessData() {
    // 第一個(gè) API 請(qǐng)求
    api1.fetch(new Callback<Data1>() {
        @Override
        public void onSuccess(Data1 data1) {
            // 第二個(gè) API 請(qǐng)求
            api2.fetch(new Callback<Data2>() {
                @Override
                public void onSuccess(Data2 data2) {
                    // 合并數(shù)據(jù)
                    ProcessedData processedData = processData(data1, data2);
                    // 展示結(jié)果
                    showResult(processedData);
                }

                @Override
                public void onFailure(Throwable throwable) {
                    // 處理錯(cuò)誤
                    handleError(throwable);
                }
            });
        }

        @Override
        public void onFailure(Throwable throwable) {
            // 處理錯(cuò)誤
            handleError(throwable);
        }
    });
}

使用 CompletionStage 的方法:

// 使用 CompletionStage 進(jìn)行鏈?zhǔn)秸{(diào)用
public void fetchAndProcessData() {
    CompletionStage<Data1> data1Future = api1.fetch();
    CompletionStage<Data2> data2Future = api2.fetch();

    data1Future.thenCombine(data2Future, this::processData) // 合并數(shù)據(jù)
        .thenAccept(this::showResult) // 展示結(jié)果
        .exceptionally(this::handleError); // 處理錯(cuò)誤
}

private ProcessedData processData(Data1 data1, Data2 data2) {
    // 合并數(shù)據(jù)邏輯
    // ... 
    return processedData;
}

private void showResult(ProcessedData processedData) {
    // 展示結(jié)果邏輯
    // ...
}

private Throwable handleError(Throwable throwable) {
    // 處理錯(cuò)誤邏輯
    // ...
    return throwable;
}

3.4、將 CompletionStage 的結(jié)果作為另一個(gè) CompletionStage 的輸入

示例:
假設(shè)我們有一個(gè)數(shù)據(jù)庫(kù)查詢(xún)操作获讳,需要先獲取用戶(hù) ID阴颖,然后再根據(jù)用戶(hù) ID 獲取用戶(hù)信息。我們可以使用 CompletionStage 的 thenCompose 方法來(lái)實(shí)現(xiàn)這個(gè)操作:

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

public class CompletionStageChain {

    public static void main(String[] args) {
        // Step 1: 獲取用戶(hù)信息
        CompletableFuture<String> user = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "用戶(hù)A";
        });

        // Step 2: 使用用戶(hù)信息獲取訂單列表
        Function<String, CompletableFuture<String>> getOrders = userStr -> CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "訂單列表:" + userStr + "的訂單信息";
        });

        // Step 3: 將用戶(hù)信息和訂單列表拼接在一起
        Function<String, String> joinUserAndOrders = orders -> "用戶(hù)信息:" + user.join() + "\n" + orders;

        // 將多個(gè) CompletionStage 連接起來(lái)
        user.thenCompose(getOrders) 
                .thenApply(joinUserAndOrders)
                .thenAccept(System.out::println)
                .exceptionally(throwable -> {
                    System.err.println("Error occurred: " + throwable.getMessage());
                    return null;
                });
    }
}

解釋:

  • 在上面的示例中赔嚎,我們使用 thenApplyuser 的結(jié)果作為 getOrders 函數(shù)的輸入膘盖,從而獲得 訂單列表
  • 接著尤误,我們使用 thenApply訂單列表 作為 joinUserAndOrders 函數(shù)的輸入侠畔,從而將用戶(hù)信息和訂單列表拼接在一起。
  • 最后损晤,我們使用 thenAccept 將最終結(jié)果輸出到控制臺(tái)软棺。

簡(jiǎn)化代碼

  • 使用CompletionStage的鏈?zhǔn)秸{(diào)用,我們可以將多個(gè)異步操作連接起來(lái)尤勋,形成一個(gè)完整的流程喘落,代碼更加簡(jiǎn)潔易讀。
  • 相比于手動(dòng)管理多個(gè) Future 對(duì)象最冰,CompletionStage 的鏈?zhǔn)秸{(diào)用可以更方便地處理異步操作的組合和處理瘦棋,提高代碼的可讀性和可維護(hù)性。

4.執(zhí)行效率

細(xì)心者會(huì)發(fā)現(xiàn)暖哨,上面經(jīng)常用到CompletableFuture.supplyAsync 方法來(lái)開(kāi)始一個(gè)異步任務(wù)赌朋,也恰恰就是在supplyAsync中,會(huì)使用一個(gè)異步線程來(lái)執(zhí)行傳入的 Supplier 函數(shù)篇裁。但是沛慢,這個(gè)異步線程并非由 CompletableFuture 自己創(chuàng)建,而是來(lái)自于 Java 中的 ForkJoinPool达布。

image.png
image.png

ForkJoinPool

定義: ForkJoinPool 是 Java 提供的一個(gè)線程池团甲,專(zhuān)門(mén)設(shè)計(jì)用于執(zhí)行可分解為更小任務(wù)的并行任務(wù)。

工作原理: ForkJoinPool 使用了一種分治的思想黍聂,將一個(gè)任務(wù)分解為多個(gè)子任務(wù)躺苦,并使用多個(gè)線程并行執(zhí)行這些子任務(wù)身腻,最終將結(jié)果合并,實(shí)現(xiàn)高效的并行計(jì)算圾另。

使用場(chǎng)景: ForkJoinPool 適用于處理那些可以分解為更小任務(wù)的任務(wù)霸株,例如:

  • 并行計(jì)算
  • 海量數(shù)據(jù)處理
  • 遞歸算法

CompletableFuture.supplyAsync 和 ForkJoinPool

CompletableFuture.supplyAsync 方法默認(rèn)使用 ForkJoinPool.commonPool() 來(lái)執(zhí)行異步任務(wù)。
ForkJoinPool.commonPool() 是一個(gè)共享的 ForkJoinPool集乔,它由所有使用 ForkJoinPool 的類(lèi)共享使用。
如果你需要更精細(xì)的控制坡椒,你也可以通過(guò) CompletableFuture.supplyAsync(supplier, executor) 方法指定一個(gè)自定義的線程池來(lái)執(zhí)行異步任務(wù)扰路。

5. 總結(jié)

  • Future 是一個(gè)基礎(chǔ)的異步操作接口,簡(jiǎn)單易用倔叼。
  • CompletionStage 提供了更加靈活的異步操作組合和處理方法汗唱,支持鏈?zhǔn)秸{(diào)用,簡(jiǎn)化代碼編寫(xiě)丈攒。
  • CompletableFutureCompletionStage 的基礎(chǔ)上提供了更豐富的功能哩罪,更方便的控制和操作,并且更加高效巡验。

根據(jù)實(shí)際需求選擇合適的異步操作機(jī)制际插,可以有效提高程序的效率和可維護(hù)性。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末显设,一起剝皮案震驚了整個(gè)濱河市框弛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捕捂,老刑警劉巖瑟枫,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異指攒,居然都是意外死亡慷妙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)允悦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膝擂,“玉大人,你說(shuō)我怎么就攤上這事澡屡≡持浚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵驶鹉,是天一觀的道長(zhǎng)绩蜻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)室埋,這世上最難降的妖魔是什么办绝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任伊约,我火速辦了婚禮,結(jié)果婚禮上孕蝉,老公的妹妹穿的比我還像新娘屡律。我一直安慰自己,他們只是感情好降淮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布超埋。 她就那樣靜靜地躺著,像睡著了一般佳鳖。 火紅的嫁衣襯著肌膚如雪霍殴。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天系吩,我揣著相機(jī)與錄音来庭,去河邊找鬼。 笑死穿挨,一個(gè)胖子當(dāng)著我的面吹牛月弛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播科盛,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼帽衙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了土涝?” 一聲冷哼從身側(cè)響起佛寿,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎但壮,沒(méi)想到半個(gè)月后冀泻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜡饵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年弹渔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溯祸。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肢专,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焦辅,到底是詐尸還是另有隱情博杖,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布筷登,位于F島的核電站剃根,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏前方。R本人自食惡果不足惜狈醉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一廉油、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苗傅,春花似錦抒线、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至摇庙,卻和暖如春旱物,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卫袒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留单匣,地道東北人夕凝。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像户秤,于是被迫代替她去往敵國(guó)和親码秉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容