Java 8 有大量的新特性和增強(qiáng)如 Lambda 表達(dá)式锹杈,Streams撵孤,CompletableFuture等。在本篇文章中我將詳細(xì)解釋清楚CompletableFuture以及它所有方法的使用竭望,API介紹參見CompletableFuture相關(guān)API
什么是CompletableFuture邪码?
在Java中CompletableFuture用于異步編程,異步編程是編寫非阻塞的代碼咬清,運(yùn)行的任務(wù)在一個(gè)單獨(dú)的線程闭专,與主線程隔離,并且會(huì)通知主線程它的進(jìn)度旧烧,成功或者失敗影钉。
在這種方式中,主線程不會(huì)被阻塞掘剪,不需要一直等到子線程完成平委。主線程可以并行的執(zhí)行其他任務(wù)。
使用這種并行方式夺谁,可以極大的提高程序的性能廉赔。
Future vs CompletableFuture
CompletableFuture 是 Future API的擴(kuò)展。
Future 被用于作為一個(gè)異步計(jì)算結(jié)果的引用匾鸥。提供一個(gè) isDone()
方法來檢查計(jì)算任務(wù)是否完成蜡塌。當(dāng)任務(wù)完成時(shí),get()
方法用來接收計(jì)算任務(wù)的結(jié)果勿负。
從 Callbale和 Future 教程可以學(xué)習(xí)更多關(guān)于 Future 知識(shí).
Future API 是非常好的 Java 異步編程進(jìn)階馏艾,但是它缺乏一些非常重要和有用的特性。
Future 的局限性
- 不能手動(dòng)完成 當(dāng)你寫了一個(gè)函數(shù)笆环,用于通過一個(gè)遠(yuǎn)程API獲取一個(gè)電子商務(wù)產(chǎn)品最新價(jià)格攒至。因?yàn)檫@個(gè) API 太耗時(shí)厚者,你把它允許在一個(gè)獨(dú)立的線程中躁劣,并且從你的函數(shù)中返回一個(gè) Future。 現(xiàn)在假設(shè)這個(gè)API服務(wù)宕機(jī)了库菲,這時(shí)你想通過該產(chǎn)品的最新緩存價(jià)格手工完成這個(gè)Future 账忘。你會(huì)發(fā)現(xiàn)無法這樣做。
- Future 的結(jié)果在非阻塞的情況下熙宇,不能執(zhí)行更進(jìn)一步的操作 Future 不會(huì)通知你它已經(jīng)完成了鳖擒,它提供了一個(gè)阻塞的
get()
方法通知你結(jié)果。 你無法給 Future 植入一個(gè)回調(diào)函數(shù)烫止,當(dāng) Future 結(jié)果可用的時(shí)候蒋荚,用該回調(diào)函數(shù)自動(dòng)的調(diào)用 Future 的結(jié)果。 - 多個(gè) Future 不能串聯(lián)在一起組成鏈?zhǔn)秸{(diào)用 有時(shí)候你需要執(zhí)行一個(gè)長時(shí)間運(yùn)行的計(jì)算任務(wù)馆蠕,并且當(dāng)計(jì)算任務(wù)完成的時(shí)候期升,你需要把它的計(jì)算結(jié)果發(fā)送給另外一個(gè)長時(shí)間運(yùn)行的計(jì)算任務(wù)等等惊奇。 你會(huì)發(fā)現(xiàn)你無法使用 Future 創(chuàng)建這樣的一個(gè)工作流。
- 不能組合多個(gè) Future 的結(jié)果 假設(shè)你有10個(gè)不同的Future播赁,你想并行的運(yùn)行颂郎,然后在它們運(yùn)行未完成后運(yùn)行一些函數(shù)。你會(huì)發(fā)現(xiàn)你也無法使用 Future 這樣做容为。
- 沒有異常處理 Future API 沒有任務(wù)的異常處理結(jié)構(gòu)
居然有如此多的限制乓序,幸好我們有CompletableFuture,你可以使用 CompletableFuture 達(dá)到以上所有目的坎背。
CompletableFuture 實(shí)現(xiàn)了 Future
和 CompletionStage
接口替劈,并且提供了許多關(guān)于創(chuàng)建,鏈?zhǔn)秸{(diào)用和組合多個(gè) Future 的便利方法集沼瘫,而且有廣泛的異常處理支持抬纸。
創(chuàng)建 CompletableFuture
1. 簡單的例子
可以使用如下無參構(gòu)造函數(shù)簡單的創(chuàng)建 CompletableFuture:
CompletableFuture<String> completableFuture = new CompletableFuture<String>();
這是一個(gè)最簡單的 CompletableFuture,想獲取CompletableFuture 的結(jié)果可以使用 CompletableFuture.get()
方法:
String result = completableFuture.get()
get()
方法會(huì)一直阻塞直到 Future 完成耿戚。因此湿故,以上的調(diào)用將被永遠(yuǎn)阻塞,因?yàn)樵揊uture一直不會(huì)完成膜蛔。
你可以使用 CompletableFuture.complete()
手工的完成一個(gè) Future:
completableFuture.complete("Future's Result")
所有等待這個(gè) Future 的客戶端都將得到一個(gè)指定的結(jié)果坛猪,并且 completableFuture.complete()
之后的調(diào)用將被忽略。
2. 使用 runAsync()
運(yùn)行異步計(jì)算
如果你想異步的運(yùn)行一個(gè)后臺(tái)任務(wù)并且不想改任務(wù)返回任務(wù)東西皂股,這時(shí)候可以使用 CompletableFuture.runAsync()
方法墅茉,它持有一個(gè)Runnable 對(duì)象,并返回 CompletableFuture<Void>
呜呐。
// Run a task specified by a Runnable Object asynchronously.
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
// Simulate a long-running Job
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("I'll run in a separate thread than the main thread.");
}
});
// Block and wait for the future to complete
future.get()
你也可以以 lambda 表達(dá)式的形式傳入 Runnable 對(duì)象:
// Using Lambda Expression
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// Simulate a long-running Job
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("I'll run in a separate thread than the main thread.");
});
在本文中就斤,我使用lambda表達(dá)式會(huì)比較頻繁,如果以前你沒有使用過蘑辑,建議你也多使用lambda 表達(dá)式洋机。
3. 使用 supplyAsync()
運(yùn)行一個(gè)異步任務(wù)并且返回結(jié)果
當(dāng)任務(wù)不需要返回任何東西的時(shí)候, CompletableFuture.runAsync()
非常有用洋魂。但是如果你的后臺(tái)任務(wù)需要返回一些結(jié)果應(yīng)該要怎么樣绷旗?
CompletableFuture.supplyAsync()
就是你的選擇。它持有supplier<T>
并且返回CompletableFuture<T>
副砍,T
是通過調(diào)用 傳入的supplier取得的值的類型衔肢。
// Run a task specified by a Supplier object asynchronously
CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of the asynchronous computation";
}
});
// Block and get the result of the Future
String result = future.get();
System.out.println(result);
Supplier<T>
是一個(gè)簡單的函數(shù)式接口,表示supplier的結(jié)果豁翎。它有一個(gè)get()
方法角骤,該方法可以寫入你的后臺(tái)任務(wù)中,并且返回結(jié)果心剥。
你可以使用lambda表達(dá)式使得上面的示例更加簡明:
// Using Lambda Expression
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of the asynchronous computation";
});
一個(gè)關(guān)于Executor 和Thread Pool筆記
你可能想知道邦尊,我們知道runAsync()
和supplyAsync()
方法在單獨(dú)的線程中執(zhí)行他們的任務(wù)硼控。但是我們不會(huì)永遠(yuǎn)只創(chuàng)建一個(gè)線程。
CompletableFuture可以從全局的 ForkJoinPool.commonPool()獲得一個(gè)線程中執(zhí)行這些任務(wù)胳赌。
但是你也可以創(chuàng)建一個(gè)線程池并傳給runAsync()
和supplyAsync()
方法來讓他們從線程池中獲取一個(gè)線程執(zhí)行它們的任務(wù)牢撼。
CompletableFuture API 的所有方法都有兩個(gè)變體-一個(gè)接受Executor
作為參數(shù),另一個(gè)不這樣:// Variations of runAsync() and supplyAsync() methods static CompletableFuture<Void> runAsync(Runnable runnable) static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
創(chuàng)建一個(gè)線程池疑苫,并傳遞給其中一個(gè)方法:
Executor executor = Executors.newFixedThreadPool(10); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new IllegalStateException(e); } return "Result of the asynchronous computation"; }, executor);
在 CompletableFuture 轉(zhuǎn)換和運(yùn)行
CompletableFuture.get()
方法是阻塞的熏版。它會(huì)一直等到Future完成并且在完成后返回結(jié)果。
但是捍掺,這是我們想要的嗎撼短?對(duì)于構(gòu)建異步系統(tǒng),我們應(yīng)該附上一個(gè)回調(diào)給CompletableFuture挺勿,當(dāng)Future完成的時(shí)候曲横,自動(dòng)的獲取結(jié)果。
如果我們不想等待結(jié)果返回不瓶,我們可以把需要等待Future完成執(zhí)行的邏輯寫入到回調(diào)函數(shù)中禾嫉。
可以使用 thenApply()
, thenAccept()
和thenRun()
方法附上一個(gè)回調(diào)給CompletableFuture。
1. thenApply()
可以使用 thenApply()
處理和改變CompletableFuture的結(jié)果蚊丐。持有一個(gè)Function<R,T>
作為參數(shù)熙参。Function<R,T>
是一個(gè)簡單的函數(shù)式接口,接受一個(gè)T類型的參數(shù)麦备,產(chǎn)出一個(gè)R類型的結(jié)果孽椰。
// Create a CompletableFuture
CompletableFuture<String> whatsYourNameFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Rajeev";
});
// Attach a callback to the Future using thenApply()
CompletableFuture<String> greetingFuture = whatsYourNameFuture.thenApply(name -> {
return "Hello " + name;
});
// Block and get the result of the future.
System.out.println(greetingFuture.get()); // Hello Rajeev
你也可以通過附加一系列的thenApply()
在回調(diào)方法 在CompletableFuture寫一個(gè)連續(xù)的轉(zhuǎn)換。這樣的話凛篙,結(jié)果中的一個(gè) thenApply
方法就會(huì)傳遞給該系列的另外一個(gè) thenApply
方法黍匾。
CompletableFuture<String> welcomeText = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Rajeev";
}).thenApply(name -> {
return "Hello " + name;
}).thenApply(greeting -> {
return greeting + ", Welcome to the CalliCoder Blog";
});
System.out.println(welcomeText.get());
// Prints - Hello Rajeev, Welcome to the CalliCoder Blog
2. thenAccept() 和 thenRun()
如果你不想從你的回調(diào)函數(shù)中返回任何東西,僅僅想在Future完成后運(yùn)行一些代碼片段呛梆,你可以使用thenAccept()
和 thenRun()
方法锐涯,這些方法經(jīng)常在調(diào)用鏈的最末端的最后一個(gè)回調(diào)函數(shù)中使用。
CompletableFuture.thenAccept()
持有一個(gè)Consumer<T>
削彬,返回一個(gè)CompletableFuture<Void>
全庸。它可以訪問CompletableFuture
的結(jié)果:
// thenAccept() example
CompletableFuture.supplyAsync(() -> {
return ProductService.getProductDetail(productId);
}).thenAccept(product -> {
System.out.println("Got product detail from remote service " + product.getName())
});
雖然thenAccept()
可以訪問CompletableFuture的結(jié)果秀仲,但thenRun()
不能訪Future的結(jié)果融痛,它持有一個(gè)Runnable返回CompletableFuture<void >
// thenRun() example
CompletableFuture.supplyAsync(() -> {
// Run some computation
}).thenRun(() -> {
// Computation Finished.
});
異步回調(diào)方法的筆記
CompletableFuture提供的所有回調(diào)方法都有兩個(gè)變體:
// thenApply() variants <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)
這些異步回調(diào)變體通過在獨(dú)立的線程中執(zhí)行回調(diào)任務(wù)幫助你進(jìn)一步執(zhí)行并行計(jì)算。
以下示例:CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new IllegalStateException(e); } return "Some Result" }).thenApply(result -> { /* Executed in the same thread where the supplyAsync() task is executed or in the main thread If the supplyAsync() task completes immediately (Remove sleep() call to verify) */ return "Processed Result" })
在以上示例中神僵,在
thenApply()
中的任務(wù)和在supplyAsync()
中的任務(wù)執(zhí)行在相同的線程中雁刷。任何supplyAsync()
立即執(zhí)行完成,那就是執(zhí)行在主線程中(嘗試刪除sleep測試下)。
為了控制執(zhí)行回調(diào)任務(wù)的線程保礼,你可以使用異步回調(diào)沛励。如果你使用thenApplyAsync()
回調(diào)责语,將從ForkJoinPool.commonPool()
獲取不同的線程執(zhí)行。CompletableFuture.supplyAsync(() -> { return "Some Result" }).thenApplyAsync(result -> { // Executed in a different thread from ForkJoinPool.commonPool() return "Processed Result" })
此外目派,如果你傳入一個(gè)Executor
到thenApplyAsync()
回調(diào)中坤候,,任務(wù)將從Executor線程池獲取一個(gè)線程執(zhí)行企蹭。
Executor executor = Executors.newFixedThreadPool(2);
CompletableFuture.supplyAsync(() -> {
return "Some result"
}).thenApplyAsync(result -> {
// Executed in a thread obtained from the executor
return "Processed Result"
}, executor);
組合兩個(gè)CompletableFuture
1. 使用 thenCompose()
組合兩個(gè)獨(dú)立的future
假設(shè)你想從一個(gè)遠(yuǎn)程API中獲取一個(gè)用戶的詳細(xì)信息白筹,一旦用戶信息可用,你想從另外一個(gè)服務(wù)中獲取他的貸方谅摄。
考慮下以下兩個(gè)方法getUserDetail()
和getCreditRating()
的實(shí)現(xiàn):
CompletableFuture<User> getUsersDetail(String userId) {
return CompletableFuture.supplyAsync(() -> {
UserService.getUserDetails(userId);
});
}
CompletableFuture<Double> getCreditRating(User user) {
return CompletableFuture.supplyAsync(() -> {
CreditRatingService.getCreditRating(user);
});
}
現(xiàn)在讓我們弄明白當(dāng)使用了thenApply()
后是否會(huì)達(dá)到我們期望的結(jié)果-
CompletableFuture<CompletableFuture<Double>> result = getUserDetail(userId)
.thenApply(user -> getCreditRating(user));
在更早的示例中徒河,Supplier
函數(shù)傳入thenApply
將返回一個(gè)簡單的值,但是在本例中送漠,將返回一個(gè)CompletableFuture顽照。以上示例的最終結(jié)果是一個(gè)嵌套的CompletableFuture。
如果你想獲取最終的結(jié)果給最頂層future闽寡,使用 thenCompose()
方法代替-
CompletableFuture<Double> result = getUserDetail(userId)
.thenCompose(user -> getCreditRating(user));
因此代兵,規(guī)則就是-如果你的回調(diào)函數(shù)返回一個(gè)CompletableFuture,但是你想從CompletableFuture鏈中獲取一個(gè)直接合并后的結(jié)果爷狈,這時(shí)候你可以使用thenCompose()
奢人。
2. 使用thenCombine()
組合兩個(gè)獨(dú)立的 future
雖然thenCompose()
被用于當(dāng)一個(gè)future依賴另外一個(gè)future的時(shí)候用來組合兩個(gè)future。thenCombine()
被用來當(dāng)兩個(gè)獨(dú)立的Future
都完成的時(shí)候淆院,用來做一些事情何乎。
System.out.println("Retrieving weight.");
CompletableFuture<Double> weightInKgFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return 65.0;
});
System.out.println("Retrieving height.");
CompletableFuture<Double> heightInCmFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return 177.8;
});
System.out.println("Calculating BMI.");
CompletableFuture<Double> combinedFuture = weightInKgFuture
.thenCombine(heightInCmFuture, (weightInKg, heightInCm) -> {
Double heightInMeter = heightInCm/100;
return weightInKg/(heightInMeter*heightInMeter);
});
System.out.println("Your BMI is - " + combinedFuture.get());
當(dāng)兩個(gè)Future都完成的時(shí)候,傳給``thenCombine()的回調(diào)函數(shù)將被調(diào)用土辩。
組合多個(gè)CompletableFuture
我們使用thenCompose()
和 thenCombine()
把兩個(gè)CompletableFuture組合在一起≈Ь龋現(xiàn)在如果你想組合任意數(shù)量的CompletableFuture,應(yīng)該怎么做拷淘?我們可以使用以下兩個(gè)方法組合任意數(shù)量的CompletableFuture各墨。
static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
1. CompletableFuture.allOf()
CompletableFuture.allOf
的使用場景是當(dāng)你一個(gè)列表的獨(dú)立future,并且你想在它們都完成后并行的做一些事情启涯。
假設(shè)你想下載一個(gè)網(wǎng)站的100個(gè)不同的頁面贬堵。你可以串行的做這個(gè)操作,但是這非常消耗時(shí)間结洼。因此你想寫一個(gè)函數(shù)黎做,傳入一個(gè)頁面鏈接,返回一個(gè)CompletableFuture松忍,異步的下載頁面內(nèi)容蒸殿。
CompletableFuture<String> downloadWebPage(String pageLink) {
return CompletableFuture.supplyAsync(() -> {
// Code to download and return the web page's content
});
}
現(xiàn)在,當(dāng)所有的頁面已經(jīng)下載完畢,你想計(jì)算包含關(guān)鍵字CompletableFuture
頁面的數(shù)量宏所∷盅蓿可以使用CompletableFuture.allOf()
達(dá)成目的。
List<String> webPageLinks = Arrays.asList(...) // A list of 100 web page links
// Download contents of all the web pages asynchronously
List<CompletableFuture<String>> pageContentFutures = webPageLinks.stream()
.map(webPageLink -> downloadWebPage(webPageLink))
.collect(Collectors.toList());
// Create a combined Future using allOf()
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
pageContentFutures.toArray(new CompletableFuture[pageContentFutures.size()])
);
使用CompletableFuture.allOf()
的問題是它返回CompletableFuture<void>爬骤。但是我們可以通過寫一些額外的代碼來獲取所有封裝的CompletableFuture結(jié)果充石。</void>
// When all the Futures are completed, call `future.join()` to get their results and collect the results in a list -
CompletableFuture<List<String>> allPageContentsFuture = allFutures.thenApply(v -> {
return pageContentFutures.stream()
.map(pageContentFuture -> pageContentFuture.join())
.collect(Collectors.toList());
});
花一些時(shí)間理解下以上代碼片段。當(dāng)所有future完成的時(shí)候霞玄,我們調(diào)用了future.join()
赫冬,因此我們不會(huì)在任何地方阻塞。
join()
方法和get()
方法非常類似溃列,這唯一不同的地方是如果最頂層的CompletableFuture完成的時(shí)候發(fā)生了異常劲厌,它會(huì)拋出一個(gè)未經(jīng)檢查的異常。
現(xiàn)在讓我們計(jì)算包含關(guān)鍵字頁面的數(shù)量听隐。
// Count the number of web pages having the "CompletableFuture" keyword.
CompletableFuture<Long> countFuture = allPageContentsFuture.thenApply(pageContents -> {
return pageContents.stream()
.filter(pageContent -> pageContent.contains("CompletableFuture"))
.count();
});
System.out.println("Number of Web Pages having CompletableFuture keyword - " +
countFuture.get());
2. CompletableFuture.anyOf()
CompletableFuture.anyOf()
和其名字介紹的一樣补鼻,當(dāng)任何一個(gè)CompletableFuture完成的時(shí)候【相同的結(jié)果類型】,返回一個(gè)新的CompletableFuture雅任。以下示例:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of Future 1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of Future 2";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of Future 3";
});
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);
System.out.println(anyOfFuture.get()); // Result of Future 2
在以上示例中风范,當(dāng)三個(gè)中的任何一個(gè)CompletableFuture完成, anyOfFuture
就會(huì)完成沪么。因?yàn)?code>future2的休眠時(shí)間最少硼婿,因此她最先完成,最終的結(jié)果將是future2
的結(jié)果禽车。
CompletableFuture.anyOf()
傳入一個(gè)Future可變參數(shù)寇漫,返回CompletableFuture<object>。CompletableFuture.anyOf()
的問題是如果你的CompletableFuture返回的結(jié)果是不同類型的殉摔,這時(shí)候你講會(huì)不知道你最終CompletableFuture是什么類型州胳。
CompletableFuture 異常處理
我們探尋了怎樣創(chuàng)建CompletableFuture,轉(zhuǎn)換它們逸月,并組合多個(gè)CompletableFuture∷ㄗ玻現(xiàn)在讓我們弄明白當(dāng)發(fā)生錯(cuò)誤的時(shí)候我們應(yīng)該怎么做。
首先讓我們明白在一個(gè)回調(diào)鏈中錯(cuò)誤是怎么傳遞的碗硬。思考下以下回調(diào)鏈:
CompletableFuture.supplyAsync(() -> {
// Code which might throw an exception
return "Some result";
}).thenApply(result -> {
return "processed result";
}).thenApply(result -> {
return "result after further processing";
}).thenAccept(result -> {
// do something with the final result
});
如果在原始的supplyAsync()
任務(wù)中發(fā)生一個(gè)錯(cuò)誤瓤湘,這時(shí)候沒有任何thenApply
會(huì)被調(diào)用并且future將以一個(gè)異常結(jié)束。如果在第一個(gè)thenApply
發(fā)生錯(cuò)誤恩尾,這時(shí)候第二個(gè)和第三個(gè)將不會(huì)被調(diào)用弛说,同樣的,future將以異常結(jié)束特笋。
1. 使用 exceptionally() 回調(diào)處理異常
exceptionally()
回調(diào)給你一個(gè)從原始Future中生成的錯(cuò)誤恢復(fù)的機(jī)會(huì)剃浇。你可以在這里記錄這個(gè)異常并返回一個(gè)默認(rèn)值。
Integer age = -1;
CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
if(age < 0) {
throw new IllegalArgumentException("Age can not be negative");
}
if(age > 18) {
return "Adult";
} else {
return "Child";
}
}).exceptionally(ex -> {
System.out.println("Oops! We have an exception - " + ex.getMessage());
return "Unknown!";
});
System.out.println("Maturity : " + maturityFuture.get());
2. 使用 handle() 方法處理異常
API提供了一個(gè)更通用的方法 - handle()
從異沉晕铮恢復(fù)虎囚,無論一個(gè)異常是否發(fā)生它都會(huì)被調(diào)用。
Integer age = -1;
CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
if(age < 0) {
throw new IllegalArgumentException("Age can not be negative");
}
if(age > 18) {
return "Adult";
} else {
return "Child";
}
}).handle((res, ex) -> {
if(ex != null) {
System.out.println("Oops! We have an exception - " + ex.getMessage());
return "Unknown!";
}
return res;
});
System.out.println("Maturity : " + maturityFuture.get());
如果異常發(fā)生蔫磨,res
參數(shù)將是 null淘讥,否則,ex
將是 null堤如。
3. 使用 whenComplete() 方法處理異常
handle和exceptionally的入?yún)⑹荈untcion蒲列,whenComplete是consumer
public CompletableFuture<T> whenComplete( BiConsumer<? super T, ? super Throwable> action)