搞定 CompletableFuture冤狡,并發(fā)異步編程和編寫串行程序還有什么區(qū)別?你們要的多圖長(zhǎng)文

  • 你有一個(gè)思想项棠,我有一個(gè)思想筒溃,我們交換后,一個(gè)人就有兩個(gè)思想

  • If you can NOT explain it simply, you do NOT understand it well enough

現(xiàn)陸續(xù)將Demo代碼和技術(shù)文章整理在一起 Github實(shí)踐精選 沾乘,方便大家閱讀查看怜奖,本文同樣收錄在此,覺(jué)得不錯(cuò)翅阵,還請(qǐng)Star??

前言

上一篇文章 不會(huì)用Java Future歪玲,我懷疑你泡茶沒(méi)我快 全面分析了 Future,通過(guò)它我們可以獲取線程的執(zhí)行結(jié)果掷匠,它雖然解決了 Runnable 的 “三無(wú)” 短板滥崩,但是它自身還是有短板:

不能手動(dòng)完成計(jì)算

假設(shè)你使用 Future 運(yùn)行子線程調(diào)用遠(yuǎn)程 API 來(lái)獲取某款產(chǎn)品的最新價(jià)格,服務(wù)器由于洪災(zāi)宕機(jī)了讹语,此時(shí)如果你想手動(dòng)結(jié)束計(jì)算钙皮,而是想返回上次緩存中的價(jià)格,這是 Future 做不到的

調(diào)用 get() 方法會(huì)阻塞程序

Future 不會(huì)通知你它的完成顽决,它提供了一個(gè)get()方法短条,程序調(diào)用該方法會(huì)阻塞直到結(jié)果可用為止,沒(méi)有辦法利用回調(diào)函數(shù)附加到Future才菠,并在Future的結(jié)果可用時(shí)自動(dòng)調(diào)用它

不能鏈?zhǔn)綀?zhí)行

燒水泡茶中茸时,通過(guò)構(gòu)造函數(shù)傳參做到多個(gè)任務(wù)的鏈?zhǔn)綀?zhí)行,萬(wàn)一有更多的任務(wù)赋访,或是任務(wù)鏈的執(zhí)行順序有變可都,對(duì)原有程序的影響都是非常大的

整合多個(gè) Future 執(zhí)行結(jié)果方式笨重

假設(shè)有多個(gè) Future 并行執(zhí)行缓待,需要在這些任務(wù)全部執(zhí)行完成之后做后續(xù)操作,F(xiàn)uture 本身是做不到的渠牲,需要借助工具類 Executors 的方法

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
<T> T invokeAny(Collection<? extends Callable<T>> tasks)

沒(méi)有異常處理

Future 同樣沒(méi)有提供很好的異常處理方案

上一篇文章看 Future 覺(jué)得是發(fā)現(xiàn)了新天地旋炒,這么一說(shuō)有感覺(jué)回到了解放前

對(duì)于 Java 后端的同學(xué),在 Java1.8 之前想實(shí)現(xiàn)異步編程签杈,還想避開(kāi)上述這些煩惱国葬,ReactiveX 應(yīng)該是一個(gè)常見(jiàn)解決方案(做Android 的應(yīng)該會(huì)有了解)。如果熟悉前端同學(xué)芹壕, ES6 Promise(男朋友的承諾)也解決了異步編程的煩惱

天下語(yǔ)言都在彼此借鑒相應(yīng)優(yōu)點(diǎn)汇四,Java 作為老牌勁旅自然也要解決上述問(wèn)題。又是那個(gè)男人踢涌,并發(fā)大師 Doug Lea 憂天下程序員之憂通孽,解天下程序員之困擾,在 Java1.8 版本(Lambda 橫空出世)中睁壁,新增了一個(gè)并發(fā)工具類 CompletableFuture背苦,它的出現(xiàn),讓人在泡茶過(guò)程中潘明,品嘗到了不一樣的味道......

幾個(gè)重要 Lambda 函數(shù)

CompletableFuture 在 Java1.8 的版本中出現(xiàn)行剂,自然也得搭上 Lambda 的順風(fēng)車,為了更好的理解 CompletableFuture钳降,這里我需要先介紹一下幾個(gè) Lambda 函數(shù)厚宰,我們只需要關(guān)注它們的以下幾點(diǎn)就可以:

  • 參數(shù)接受形式
  • 返回值形式
  • 函數(shù)名稱

Runnable

Runnable 我們已經(jīng)說(shuō)過(guò)無(wú)數(shù)次了,無(wú)參數(shù)遂填,無(wú)返回值

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Function

Function<T, R> 接受一個(gè)參數(shù)铲觉,并且有返回值

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

Consumer

Consumer<T> 接受一個(gè)參數(shù),沒(méi)有返回值

@FunctionalInterface
public interface Consumer<T> {   
    void accept(T t);
}

Supplier

Supplier<T> 沒(méi)有參數(shù)吓坚,有一個(gè)返回值

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

BiConsumer

BiConsumer<T, U> 接受兩個(gè)參數(shù)(Bi撵幽, 英文單詞詞根,代表兩個(gè)的意思)礁击,沒(méi)有返回值

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);

好了盐杂,我們做個(gè)小匯總

有些同學(xué)可能有疑問(wèn),為什么要關(guān)注這幾個(gè)函數(shù)式接口哆窿,因?yàn)?CompletableFuture 的函數(shù)命名以及其作用都是和這幾個(gè)函數(shù)式接口高度相關(guān)的链烈,一會(huì)你就會(huì)發(fā)現(xiàn)了

前戲做足,終于可以進(jìn)入正題了 CompletableFuture

CompletableFuture

類結(jié)構(gòu)

老規(guī)矩更耻,先從類結(jié)構(gòu)看起:

實(shí)現(xiàn)了 Future 接口

實(shí)現(xiàn)了 Future 接口测垛,那就具有 Future 接口的相關(guān)特性,請(qǐng)腦補(bǔ) Future 那少的可憐的 5 個(gè)方法秧均,這里不再贅述食侮,具體請(qǐng)查看 不會(huì)用Java Future,我懷疑你泡茶沒(méi)我快

實(shí)現(xiàn)了 CompletionStage 接口

CompletionStage 這個(gè)接口還是挺陌生的目胡,中文直譯過(guò)來(lái)是【竣工階段】锯七,如果將燒水泡茶比喻成一項(xiàng)大工程,他們的竣工階段體現(xiàn)是不一樣的

  1. 單看線程1 或單看線程 2 就是一種串行關(guān)系誉己,做完一步之后做下一步

  2. 一起看線程1 和 線程 2眉尸,它們彼此就是并行關(guān)系,兩個(gè)線程做的事彼此獨(dú)立互補(bǔ)干擾

  3. 泡茶就是線程1 和 線程 2 的匯總/組合巨双,也就是線程 1 和 線程 2 都完成之后才能到這個(gè)階段(當(dāng)然也存在線程1 或 線程 2 任意一個(gè)線程竣工就可以開(kāi)啟下一階段的場(chǎng)景)

所以噪猾,CompletionStage 接口的作用就做了這點(diǎn)事,所有函數(shù)都用于描述任務(wù)的時(shí)序關(guān)系筑累,總結(jié)起來(lái)就是這個(gè)樣子:

CompletableFuture 既然實(shí)現(xiàn)了兩個(gè)接口袱蜡,自然也就會(huì)實(shí)現(xiàn)相應(yīng)的方法充分利用其接口特性,我們走進(jìn)它的方法來(lái)看一看

CompletableFuture 大約有50種不同處理串行慢宗,并行坪蚁,組合以及處理錯(cuò)誤的方法。小弟屏幕不爭(zhēng)氣镜沽,方法之多敏晤,一個(gè)屏幕裝不下,看到這么多方法缅茉,是不是瞬間要直接 收藏——>吃灰 2連走人嘴脾?別擔(dān)心,我們按照相應(yīng)的命名和作用進(jìn)行分類蔬墩,分分鐘搞定50多種方法

串行關(guān)系

then 直譯【然后】统阿,也就是表示下一步,所以通常是一種串行關(guān)系體現(xiàn), then 后面的單詞(比如 run /apply/accept)就是上面說(shuō)的函數(shù)式接口中的抽象方法名稱了筹我,它的作用和那幾個(gè)函數(shù)式接口的作用是一樣一樣滴

CompletableFuture<Void> thenRun(Runnable action)
CompletableFuture<Void> thenRunAsync(Runnable action)
CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
  
<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
  
CompletableFuture<Void> thenAccept(Consumer<? super T> action) 
CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
  
<U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)  
<U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
<U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor)

聚合 And 關(guān)系

combine... with...both...and... 都是要求兩者都滿足扶平,也就是 and 的關(guān)系了

<U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
<U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
<U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)

<U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
<U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
<U> CompletableFuture<Void> thenAcceptBothAsync( CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor)
  
CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor)

聚合 Or 關(guān)系

Either...or... 表示兩者中的一個(gè),自然也就是 Or 的體現(xiàn)了

<U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
<U> CompletableFuture<U> applyToEitherAsync(蔬蕊、CompletionStage<? extends T> other, Function<? super T, U> fn)
<U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn, Executor executor)

CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)

CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)

異常處理

CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
CompletableFuture<T> exceptionallyAsync(Function<Throwable, ? extends T> fn)
CompletableFuture<T> exceptionallyAsync(Function<Throwable, ? extends T> fn, Executor executor)
        
CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
        
       
<U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
<U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
<U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)

這個(gè)異常處理看著還挺嚇人的督弓,拿傳統(tǒng)的 try/catch/finally 做個(gè)對(duì)比也就瞬間秒懂了

whenComplete 和 handle 的區(qū)別如果你看接受的參數(shù)函數(shù)式接口名稱你也就能看出差別了,前者使用Comsumer, 自然也就不會(huì)有返回值猜谚;后者使用 Function隘道,自然也就會(huì)有返回值

這里并沒(méi)有全部列舉,不過(guò)相信很多同學(xué)已經(jīng)發(fā)現(xiàn)了規(guī)律:

CompletableFuture 提供的所有回調(diào)方法都有兩個(gè)異步(Async)變體猜扮,都像這樣

// thenApply() 的變體
<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

另外,方法的名稱也都與前戲中說(shuō)的函數(shù)式接口完全匹配勉吻,按照這中規(guī)律分類之后,這 50 多個(gè)方法看起來(lái)是不是很輕松了呢旅赢?

基本方法已經(jīng)羅列的差不多了齿桃,接下來(lái)我們通過(guò)一些例子來(lái)實(shí)際演示一下:

案例演示

創(chuàng)建一個(gè) CompletableFuture 對(duì)象

創(chuàng)建一個(gè) CompletableFuture 對(duì)象并沒(méi)有什么稀奇的惑惶,依舊是通過(guò)構(gòu)造函數(shù)構(gòu)建

CompletableFuture<String> completableFuture = new CompletableFuture<String>();

這是最簡(jiǎn)單的 CompletableFuture 對(duì)象創(chuàng)建方式,由于它實(shí)現(xiàn)了 Future 接口短纵,所以自然就可以通過(guò) get() 方法獲取結(jié)果

String result = completableFuture.get();

文章開(kāi)頭已經(jīng)說(shuō)過(guò)带污,get()方法在任務(wù)結(jié)束之前將一直處在阻塞狀態(tài),由于上面創(chuàng)建的 Future 沒(méi)有返回香到,所以在這里調(diào)用 get() 將會(huì)永久性的堵塞

這時(shí)就需要我們調(diào)用 complete() 方法手動(dòng)的結(jié)束一個(gè) Future

completableFuture.complete("Future's Result Here Manually");

這時(shí)鱼冀,所有等待這個(gè) Future 的 client 都會(huì)返回手動(dòng)結(jié)束的指定結(jié)果

runAsync

使用 runAsync 進(jìn)行異步計(jì)算

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    System.out.println("運(yùn)行在一個(gè)單獨(dú)的線程當(dāng)中");
});

future.get();

由于使用的是 Runnable 函數(shù)式表達(dá)式,自然也不會(huì)獲取到結(jié)果

supplyAsync

使用 runAsync 是沒(méi)有返回結(jié)果的悠就,我們想獲取異步計(jì)算的返回結(jié)果需要使用 supplyAsync() 方法

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            log.info("運(yùn)行在一個(gè)單獨(dú)的線程當(dāng)中");
            return "我有返回值";
        });

        log.info(future.get());

由于使用的是 Supplier 函數(shù)式表達(dá)式千绪,自然可以獲得返回結(jié)果

我們已經(jīng)多次說(shuō)過(guò),get() 方法在Future 計(jì)算完成之前會(huì)一直處在 blocking 狀態(tài)下梗脾,對(duì)于真正的異步處理荸型,我們希望的是可以通過(guò)傳入回調(diào)函數(shù),在Future 結(jié)束時(shí)自動(dòng)調(diào)用該回調(diào)函數(shù)藐唠,這樣帆疟,我們就不用等待結(jié)果

CompletableFuture<String> comboText = CompletableFuture.supplyAsync(() -> {
        //可以注釋掉做快速返回 start
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            log.info("??");
        //可以注釋掉做快速返回 end
            return "贊";
        })
                .thenApply(first -> {
                    log.info("在看");
                    return first + ", 在看";
                })
                .thenApply(second -> second + ", 轉(zhuǎn)發(fā)");

        log.info("三連有沒(méi)有?");
        log.info(comboText.get());

對(duì) thenApply 的調(diào)用并沒(méi)有阻塞程序打印log宇立,也就是前面說(shuō)的通過(guò)回調(diào)通知機(jī)制踪宠, 這里你看到 thenApply 使用的是supplyAsync所用的線程,如果將supplyAsync 做快速返回妈嘹,我們?cè)賮?lái)看一下運(yùn)行結(jié)果:

thenApply 此時(shí)使用的是主線程柳琢,所以:

串行的后續(xù)操作并不一定會(huì)和前序操作使用同一個(gè)線程

thenAccept

如果你不想從回調(diào)函數(shù)中返回任何結(jié)果,那可以使用 thenAccept

        final CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(
                // 模擬遠(yuǎn)端API調(diào)用润脸,這里只返回了一個(gè)構(gòu)造的對(duì)象
                () -> Product.builder().id(12345L).name("頸椎/腰椎治療儀").build())
                .thenAccept(product -> {
                    log.info("獲取到遠(yuǎn)程API產(chǎn)品名稱 " + product.getName());
                });
        voidCompletableFuture.get();

thenRun

thenAccept 可以從回調(diào)函數(shù)中獲取前序執(zhí)行的結(jié)果柬脸,但thenRun 卻不可以,因?yàn)樗幕卣{(diào)函數(shù)式表達(dá)式定義中沒(méi)有任何參數(shù)

CompletableFuture.supplyAsync(() -> {
    //前序操作
}).thenRun(() -> {
    //串行的后需操作毙驯,無(wú)參數(shù)也無(wú)返回值
});

我們前面同樣說(shuō)過(guò)了倒堕,每個(gè)提供回調(diào)方法的函數(shù)都有兩個(gè)異步(Async)變體,異步就是另外起一個(gè)線程

        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            log.info("前序操作");
            return "前需操作結(jié)果";
        }).thenApplyAsync(result -> {
            log.info("后續(xù)操作");
            return "后續(xù)操作結(jié)果";
        });

到這里爆价,相信你串行的操作你已經(jīng)非常熟練了

thenCompose

日常的任務(wù)中垦巴,通常定義的方法都會(huì)返回 CompletableFuture 類型,這樣會(huì)給后續(xù)操作留有更多的余地铭段,假如有這樣的業(yè)務(wù)(X唄是不是都有這樣的業(yè)務(wù)呢骤宣?):

//獲取用戶信息詳情
    CompletableFuture<User> getUsersDetail(String userId) {
        return CompletableFuture.supplyAsync(() -> User.builder().id(12345L).name("日拱一兵").build());
    }

    //獲取用戶信用評(píng)級(jí)
    CompletableFuture<Double> getCreditRating(User user) {
        return CompletableFuture.supplyAsync(() -> CreditRating.builder().rating(7.5).build().getRating());
    }

這時(shí),如果我們還是使用 thenApply() 方法來(lái)描述串行關(guān)系序愚,返回的結(jié)果就會(huì)發(fā)生 CompletableFuture 的嵌套

        CompletableFuture<CompletableFuture<Double>> result = completableFutureCompose.getUsersDetail(12345L)
                .thenApply(user -> completableFutureCompose.getCreditRating(user));

顯然這不是我們想要的憔披,如果想“拍平” 返回結(jié)果,thenCompose 方法就派上用場(chǎng)了

CompletableFuture<Double> result = completableFutureCompose.getUsersDetail(12345L)
                .thenCompose(user -> completableFutureCompose.getCreditRating(user));

這個(gè)和 Lambda 的map 和 flatMap 的道理是一樣一樣滴

thenCombine

如果要聚合兩個(gè)獨(dú)立 Future 的結(jié)果,那么 thenCombine 就會(huì)派上用場(chǎng)了

        CompletableFuture<Double> weightFuture = CompletableFuture.supplyAsync(() -> 65.0);
        CompletableFuture<Double> heightFuture = CompletableFuture.supplyAsync(() -> 183.8);
        
        CompletableFuture<Double> combinedFuture = weightFuture
                .thenCombine(heightFuture, (weight, height) -> {
                    Double heightInMeter = height/100;
                    return weight/(heightInMeter*heightInMeter);
                });

        log.info("身體BMI指標(biāo) - " + combinedFuture.get());

當(dāng)然這里多數(shù)時(shí)處理兩個(gè) Future 的關(guān)系芬膝,如果超過(guò)兩個(gè)Future望门,如何處理他們的一些聚合關(guān)系呢?

allOf | anyOf

相信你看到方法的簽名蔗候,你已經(jīng)明白他的用處了怒允,這里就不再介紹了

static CompletableFuture<Void>   allOf(CompletableFuture<?>... cfs)
static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

接下來(lái)就是異常的處理了

exceptionally

        Integer age = -1;

        CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
            if( age < 0 ) {
                throw new IllegalArgumentException("何方神圣埂软?");
            }
            if(age > 18) {
                return "大家都是成年人";
            } else {
                return "未成年禁止入內(nèi)";
            }
        }).thenApply((str) -> {
            log.info("游戲開(kāi)始");
            return str;
        }).exceptionally(ex -> {
            log.info("必有蹊蹺锈遥,來(lái)者" + ex.getMessage());
            return "Unknown!";
        });

        log.info(maturityFuture.get());

exceptionally 就相當(dāng)于 catch,出現(xiàn)異常勘畔,將會(huì)跳過(guò) thenApply 的后續(xù)操作所灸,直接捕獲異常,進(jìn)行一場(chǎng)處理

handle

用多線程炫七,良好的習(xí)慣是使用 try/finally 范式爬立,handle 就可以起到 finally 的作用,對(duì)上述程序做一個(gè)小小的更改万哪, handle 接受兩個(gè)參數(shù)侠驯,一個(gè)是正常返回值,一個(gè)是異常

注意:handle的寫法也算是范式的一種

        Integer age = -1;

        CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
            if( age < 0 ) {
                throw new IllegalArgumentException("何方神圣奕巍?");
            }
            if(age > 18) {
                return "大家都是成年人";
            } else {
                return "未成年禁止入內(nèi)";
            }
        }).thenApply((str) -> {
            log.info("游戲開(kāi)始");
            return str;
        }).handle((res, ex) -> {
            if(ex != null) {
                log.info("必有蹊蹺吟策,來(lái)者" + ex.getMessage());
                return "Unknown!";
            }
            return res;
        });

        log.info(maturityFuture.get());

到這里,關(guān)于 CompletableFuture 的基本使用你已經(jīng)了解的差不多了的止,不知道你是否注意檩坚,我們前面說(shuō)的帶有 Sync 的方法是單獨(dú)起一個(gè)線程來(lái)執(zhí)行,但是我們并沒(méi)有創(chuàng)建線程诅福,這是怎么實(shí)現(xiàn)的呢匾委?

細(xì)心的朋友如果仔細(xì)看每個(gè)變種函數(shù)的第三個(gè)方法也許會(huì)發(fā)現(xiàn)里面都有一個(gè) Executor 類型的參數(shù),用于指定線程池氓润,因?yàn)閷?shí)際業(yè)務(wù)中我們是嚴(yán)謹(jǐn)手動(dòng)創(chuàng)建線程的赂乐,這在 我會(huì)手動(dòng)創(chuàng)建線程,為什么要使用線程池?文章中明確說(shuō)明過(guò)咖气;如果沒(méi)有指定線程池挨措,那自然就會(huì)有一個(gè)默認(rèn)的線程池,也就是 ForkJoinPool

private static final Executor ASYNC_POOL = USE_COMMON_POOL ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

ForkJoinPool 的線程數(shù)默認(rèn)是 CPU 的核心數(shù)采章。但是运嗜,在前序文章中明確說(shuō)明過(guò):

不要所有業(yè)務(wù)共用一個(gè)線程池,因?yàn)槊踔郏坏┯腥蝿?wù)執(zhí)行一些很慢的 I/O 操作担租,就會(huì)導(dǎo)致線程池中所有線程都阻塞在 I/O 操作上,從而造成線程饑餓抵怎,進(jìn)而影響整個(gè)系統(tǒng)的性能

總結(jié)

CompletableFuture 的方法并沒(méi)有全部介紹完全奋救,也沒(méi)必要全部介紹岭参,相信大家按照這個(gè)思路來(lái)理解 CompletableFuture 也不會(huì)有什么大問(wèn)題了,剩下的就交給實(shí)踐/時(shí)間以及自己的體會(huì)了

后記

你以為 JDK1.8 CompletableFuture 已經(jīng)很完美了是不是尝艘,但追去完美的道路上永無(wú)止境演侯,Java 9 對(duì)CompletableFuture 又做了部分升級(jí)和改造

  1. 添加了新的工廠方法

  2. 支持延遲和超時(shí)處理

    orTimeout()
    completeOnTimeout()
    
  3. 改進(jìn)了對(duì)子類的支持

詳情可以查看: Java 9 CompletableFuture API Improvements. 怎樣快速的切換不同 Java 版本來(lái)嘗鮮?SDKMAN 統(tǒng)一靈活管理多版本Java 這篇文章的方法送給你

最后咱們?cè)倥菀粔夭璞澈ィ惺芤幌滦伦兓?/p>

靈魂追問(wèn)

  1. 聽(tīng)說(shuō) ForkJoinPool 線程池效率更高秒际,為什么呢?
  2. 如果批量處理異步程序狡汉,有什么可用的方案嗎娄徊?

參考

  1. Java 并發(fā)編程實(shí)戰(zhàn)
  2. Java 并發(fā)編程的藝術(shù)
  3. Java 并發(fā)編程之美
  4. https://www.baeldung.com/java-completablefuture
  5. https://www.callicoder.com/java-8-completablefuture-tutorial/
    個(gè)人博客:https://dayarch.top
    加我微信好友, 進(jìn)群娛樂(lè)學(xué)習(xí)交流,備注「進(jìn)群」

歡迎持續(xù)關(guān)注公眾號(hào):「日拱一兵」

  • 前沿 Java 技術(shù)干貨分享
  • 高效工具匯總 | 回復(fù)「工具」
  • 面試問(wèn)題分析與解答
  • 技術(shù)資料領(lǐng)取 | 回復(fù)「資料」

以讀偵探小說(shuō)思維輕松趣味學(xué)習(xí) Java 技術(shù)棧相關(guān)知識(shí)盾戴,本著將復(fù)雜問(wèn)題簡(jiǎn)單化寄锐,抽象問(wèn)題具體化和圖形化原則逐步分解技術(shù)問(wèn)題,技術(shù)持續(xù)更新尖啡,請(qǐng)持續(xù)關(guān)注......


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末橄仆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衅斩,更是在濱河造成了極大的恐慌盆顾,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛渴,死亡現(xiàn)場(chǎng)離奇詭異椎扬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)具温,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門蚕涤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人铣猩,你說(shuō)我怎么就攤上這事揖铜。” “怎么了达皿?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵天吓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我峦椰,道長(zhǎng)龄寞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任汤功,我火速辦了婚禮物邑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己色解,他們只是感情好茂嗓,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著科阎,像睡著了一般述吸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锣笨,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天蝌矛,我揣著相機(jī)與錄音,去河邊找鬼票唆。 笑死朴读,一個(gè)胖子當(dāng)著我的面吹牛屹徘,可吹牛的內(nèi)容都是我干的走趋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼噪伊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼簿煌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鉴吹,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤姨伟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后豆励,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夺荒,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年良蒸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了技扼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫩痰,死狀恐怖剿吻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情串纺,我是刑警寧澤丽旅,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站纺棺,受9級(jí)特大地震影響榄笙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祷蝌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一茅撞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦乡翅、人聲如沸鳞疲。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尚洽。三九已至,卻和暖如春靶累,著一層夾襖步出監(jiān)牢的瞬間腺毫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工挣柬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留潮酒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓邪蛔,卻偏偏與公主長(zhǎng)得像急黎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侧到,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344