你有一個(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 或單看線程 2 就是一種串行關(guān)系誉己,做完一步之后做下一步
一起看線程1 和 線程 2眉尸,它們彼此就是并行關(guān)系,兩個(gè)線程做的事彼此獨(dú)立互補(bǔ)干擾
泡茶就是線程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í)和改造
添加了新的工廠方法
-
支持延遲和超時(shí)處理
orTimeout() completeOnTimeout()
改進(jìn)了對(duì)子類的支持
詳情可以查看: Java 9 CompletableFuture API Improvements. 怎樣快速的切換不同 Java 版本來(lái)嘗鮮?SDKMAN 統(tǒng)一靈活管理多版本Java 這篇文章的方法送給你
最后咱們?cè)倥菀粔夭璞澈ィ惺芤幌滦伦兓?/p>
靈魂追問(wèn)
- 聽(tīng)說(shuō) ForkJoinPool 線程池效率更高秒际,為什么呢?
- 如果批量處理異步程序狡汉,有什么可用的方案嗎娄徊?
參考
- Java 并發(fā)編程實(shí)戰(zhàn)
- Java 并發(fā)編程的藝術(shù)
- Java 并發(fā)編程之美
- https://www.baeldung.com/java-completablefuture
-
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)注......