在多線程中,使用線程池時使用Future獲得異步執行結果時,要麼調用阻塞方法get(),要麼輪詢看isDone()是否爲true,這兩種方法都不是很好,因爲主線程也會被迫等待。
從Java 8開始引入了CompletableFuture,它針對Future做了改進,可以傳入回調對象,當異步任務完成或者發生異常時,自動調用回調對象的回調方法。
一.CompletableFuture簡介
Future可明確地完成(設定其值和狀態),並且可以被用作CompletionStage,支持相關的功能和動作的,其完成後觸發。
當兩個或多個線程嘗試對 complete, completeExceptionally或 cancel CompletableFuture進行操作時,只有其中一個成功。
除了直接操作狀態和結果的這些方法和相關方法之外,CompletableFuture還CompletionStage使用以下策略實現接口:
- 爲非異步方法的相關完成提供的動作 可以由完成當前CompletableFuture的線程執行,也可以由完成方法的任何其他調用者執行。
- 所有沒有顯式Executor參數的異步方法都使用來執行ForkJoinPool.commonPool() (除非它不支持並行度至少爲2,在這種情況下,將創建一個新的Thread來運行每個任務)。爲了簡化監視,調試和跟蹤,所有生成的異步任務都是標記接口的實例CompletableFuture.AsynchronousCompletionTask。
- 所有CompletionStage方法都是獨立於其他公共方法實現的,因此一個方法的行爲不受子類中其他方法的覆蓋影響。
CompletableFuture還Future採用以下策略實施:
- 由於(與FutureTask此類不同)此類無法直接控制導致其完成的計算,因此取消被視爲異常完成的另一種形式。方法cancel具有與相同的效果 completeExceptionally(new CancellationException())。方法 isCompletedExceptionally()可用於確定CompletableFuture是否以任何特殊方式完成。
- 如果使用CompletionException異常完成,則方法get()和get(long, TimeUnit)拋出具有 ExecutionException與相應CompletionException中所保存的原因相同的原因。爲了簡化大多數情況下的用法,此類還定義了方法,join()並且 getNow(T)在這些情況下直接拋出CompletionException。
二.常用Api介紹與實戰
2.1 runAsync
- runAsync(Runnable runnable)
返回一個新的CompletableFuture,它在運行給定操作後由運行在 ForkJoinPool.commonPool()中的任務 異步完成。 - runAsync(Runnable runnable, Executor executor)
返回一個新的CompletableFuture,它在運行給定操作之後由在給定執行程序中運行的任務異步完成。
代碼示例
@SneakyThrows
public static void main(String[] args) {
CompletableFuture<Void> cf2 = CompletableFuture.runAsync(() -> {
set(6.0, 10.0);
});
Thread.sleep(2000);
}
public static Double set(Double x,Double y){
System.out.println("X="+x + "---- Y="+y);
return Double.sum(x,y);
}
2.2 supplyAsync
- supplyAsync(Supplier supplier)
返回一個新的CompletableFuture,它通過在 ForkJoinPool.commonPool()中運行的任務與通過調用給定的供應商獲得的值 異步完成。 - supplyAsync(Supplier supplier, Executor executor)
返回一個新的CompletableFuture,由給定執行器中運行的任務異步完成,並通過調用給定的供應商獲得的值
代碼示例
@SneakyThrows
public static void main(String[] args) {
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
return set(6.0, 10.0);
});
Thread.sleep(2000);
}
public static Double set(Double x,Double y){
System.out.println("X="+x + "---- Y="+y);
return Double.sum(x,y);
}
2.3 thenAccept和exceptionally
- thenAccept(Consumer<? super T> action)
返回一個新的CompletionStage,當此階段正常完成時,將以該階段的結果作爲提供的操作的參數執行。 - exceptionally(Function<Throwable,? extends T> fn)
返回一個新的CompletableFuture,當CompletableFuture完成時完成,結果是異常觸發此CompletableFuture的完成特殊功能的給定功能; 否則,如果此CompletableFuture正常完成,則返回的CompletableFuture也會以相同的值正常完成。
代碼示例
@SneakyThrows
public static void main(String[] args) {
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
return set(6.0, 10.0);
});
//成功後執行
cf2.thenAccept((sum)->{
System.out.println("最終的值爲:"+sum);
});
//失敗執行
cf2.exceptionally((e)->{
e.printStackTrace();
return null;
});
Thread.sleep(2000);
}
public static Double set(Double x,Double y){
System.out.println("X="+x + "---- Y="+y);
return Double.sum(x,y);
}
2.4 串行與並行
2.4.1 串行
@SneakyThrows
public static void main(String[] args) {
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
return set(6.0, 10.0);
});
//成功後執行 執行下一個
CompletableFuture<Double> cf3 = cf2.thenApplyAsync((setUp) -> {
return setUp(10.0,9.98);
});
cf2.thenAccept(sum-> System.out.println("sum:"+sum));
cf3.thenAccept(sum-> System.out.println("setUp:"+sum));
//失敗執行
cf2.exceptionally((e)->{
e.printStackTrace();
return null;
});
Thread.sleep(2000);
}
@SneakyThrows
public static Double set(Double x, Double y){
System.out.println("X="+x + "---- Y="+y);
Thread.sleep(500);
return Double.sum(x,y);
}
@SneakyThrows
public static Double setUp(Double x, Double y){
System.out.println("X="+x + "---- Y="+y);
Thread.sleep(500);
return Double.sum(x,y);
}
執行結果
2.4.2 並行
- allOf(CompletableFuture<?>… cfs)
返回一個新的CompletableFuture,當所有給定的CompletableFutures完成時,完成。 - anyOf(CompletableFuture<?>… cfs)
返回一個新的CompletableFuture,當任何一個給定的CompletableFutures完成時,完成相同的結果。
@SneakyThrows
public static void main(String[] args) {
//創建兩個異步的 CompletableFuture
CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
return set(6.0, 10.0);
});
CompletableFuture<Double> cf3 = CompletableFuture.supplyAsync(() -> {
return setUp(10.0,9.98);
});
//將 cf2 cf3 進行合併
CompletableFuture<Object> objcf = CompletableFuture.anyOf(cf2, cf3);
// 分別執行 ,獲取 相應的返回值
CompletableFuture<Object> obj1 = objcf.thenApplyAsync(set -> {
return set;
});
CompletableFuture<Object> obj2 = objcf.thenApplyAsync(setUp -> {
return setUp;
});
//在次合併
CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(obj1, obj2);
//統一sum
objectCompletableFuture.thenAccept(result->{
System.out.println("sum:"+result);
});
Thread.sleep(2000);
}
@SneakyThrows
public static Double set(Double x, Double y){
System.out.println("X="+x + "---- Y="+y);
Thread.sleep(500);
return Double.sum(x,y);
}
@SneakyThrows
public static Double setUp(Double x, Double y){
System.out.println("X="+x + "---- Y="+y);
Thread.sleep(500);
return Double.sum(x,y);
}
本文的分享暫時就到這裏,希望對您有所幫助
關注 Java有貨領取更多資料
聯繫小編。微信:372787553,帶您進羣互相學習
左側小編微信,右側獲取免費資料
- SpringCloud 自定義封裝架構https://github.com/Dylan-haiji/javayh-platform
- Java 設計模式學習代碼 https://github.com/Dylan-haiji/design-pattern
- SpringCloud學習代碼: https://github.com/Dylan-haiji/javayh-cloud
- AlibabaCloud學習代碼:https://github.com/Dylan-haiji/javayh-cloud-nacos
- SpringBoot+Mybatis 多數據源切換:https://github.com/Dylan-haiji/javayh-boot-data-soure
- Redis、Mongo、Rabbitmq、Kafka學習代碼: https://github.com/Dylan-haiji/javayh-middleware
- SpringBoot+SpringSecurity實現自定義登錄學習代碼:https://github.com/Dylan-haiji/javayh-distribution