CompletableFuture 組合式非同步程式設計
最近學習dubbo 原始碼, 敲了些vertx 程式碼,覺得非同步程式設計的風格是有多麼的帥,lambda表示式寫的美又很有邏輯。java 強語言能寫成這樣也是很美的一件事情。
這遍文章簡單的看看CompletableFuture 是怎麼使用的,背後都幹了啥事,先看看dubbo上的completableFuture。Dubbo 的future 使用,請看這篇部落格http://dubbo.apache.org/en-us/blog/dubbo-new-async.html, 一直到dubbo 2.7.0 才出現 CompletableFuture,dubbo 2.6.x 會報序列化的錯誤, 因為 CompletableFuture<object> 不是一個序列化的物件,沒有 implement serialization.
(1). 首先看看 CompletableFuture 有哪些基本的用法。CompletableFuture 是一個class,實現future 和 CompletionStage(完成階段) 介面。多個CompletionStage可以有先後順序, 可以 多個任務同時完成,可以上一個任務執行的結果傳給下個任務。有人說是執行緒編排,也有其道理。 CompletableFuture 不支援使用 callable,而支援Runnable 和 supplier, 這些都是被@FunctionalInterface 註解標註的介面。 有lambda 的表達特性。supplier 代替 callable 支援 有返回結果的非同步。
我們看一個例子:
ExecutorService executor = Executors.newFixedThreadPool(3); // 預設可以通過ForkJoinPool 產生的執行緒來執行任務,其執行緒數是 CPU 核數➖1,不然就使用 ThreadPerTaskExecutor
Supplier<Integer> externalTask = () -> {
// do something
return 3;
}
CompletableFuture.supplyAsync(externalTask, executor).
我們發現其本質是使用executor 執行緒 執行 externalTask的任務,任務只完成之後將結果放到 Future 中,通過get 方法獲取結果。Future get 的方法是同步還是非同步呢? 我覺得會阻塞,一直拿到正確的結果(異常, 或者 執行緒計算出來的結果), 它和 completableFuture join 方法類似。對於Runnable 介面, 可使用 runAsync方法。
(2) 那麼 CompletableFuture 如何觸發 ConpletionStage 呢? 我們來看看
public boolean complete(T value), public boolean completeExceptionally(Throwable ex) 。 我們發現在原始碼中或者別的非同步框架中,比如前端的promise,當任務完成之後,會設定 complete,表示任務完成成功或者失敗。complete會觸發依賴他們的completionStage。下面是java 部分的程式碼,說實話我有點看不懂,好像是將依賴 放入 stack 中,然後一個一個依賴觸發
*/
final void postComplete() {
/*
* On each step, variable f holds current dependents to pop
* and run. It is extended along only one path at a time,
* pushing others to avoid unbounded recursion.
*/
CompletableFuture f =this; Completion h;
while ((h = f.stack) !=null ||
(f !=this && (h = (f =this).stack) !=null)) {
CompletableFuture d; Completion t;
if (f.casStack(h, t = h.next)) {
if (t !=null) {
if (f !=this) {
pushStack(h);
continue;
}
h.next =null; // detach
}
f = (d = h.tryFire(NESTED)) ==null ?this : d;
}
}
}。
我們看一下
public CompletableFuture<T> whenComplete(
BiConsumer<? super T, ? super Throwable> action)。 其中action 表示回撥函式,型別是BiConsumer,接收 結果 和異常。那麼我們如何來寫一個有順序性的任務呢? completableFuture.supplyAsync(externalTask).whenComplete((result, ex) ->{
// result 是上一個task 執行的結果
//ex執行的異常
}), 但是這個方法可能會造成同步阻塞,因為 supplyAsync 和 後面的 whenComplete 是同一個執行緒執行的,當然也有可能是不是同個執行緒執行,看註冊時, supplyAsync 這個執行緒結束沒有。我們可以使用 下面的 方法來非同步執行
public CompletableFuture whenCompleteAsync(
BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture whenCompleteAsync(
BiConsumer<? super T, ? super Throwable> action, Executor executor)
但是如果我們只對異常結果感興趣,我們可以使用下面的方法
public CompletableFuture<T> exceptionally(
Function<Throwable, ? extends T> fn)
(3) : 構建依賴
無結果的依賴呼叫: CompletableFuture.runAsync(taskA).thenRun(taskB).thenRun(taskC).join // 非同步版本的是 thenRunAsync
有結果的依賴呼叫:
// Supplier<String> taskA = () -> "hello"; Function<String, String> taskB = (t) -> t.toUpperCase(); Consumer<String> taskC = (t) -> System.out.println("consume: " + t);
CompletableFuture.supplyAsync(taskA)
.thenApply(taskB)
.thenAccept(taskC)
.join();
runAfterBoth(對應任務Runnable), thenCombine(對應任務BiFunction), thenAcceptBoth(對應任務型別BiConsumer)
Supplier<String> taskA = () -> "taskA";
CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() -> "taskB");
BiFunction<String, String, String> taskC = (a, b) -> a + "," + b;
String ret = CompletableFuture.supplyAsync(taskA)
.thenCombineAsync(taskB, taskC)
.join();
其中還有更為神奇的是allOf 和 anyOf, 列子都是來自於老馬程式設計的,寫的著實很好。
CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {
delayRandom(100, 1000);
return "helloA";
}, executor);
CompletableFuture<Void> taskB = CompletableFuture.runAsync(() -> {
delayRandom(2000, 3000);
}, executor);
CompletableFuture<Void> taskC = CompletableFuture.runAsync(() -> {
delayRandom(30, 100);
throw new RuntimeException("task C exception");
}, executor);
CompletableFuture.allOf(taskA, taskB, taskC).whenComplete((result, ex) -> {
if (ex != null) {
System.out.println(ex.getMessage());
}
if (!taskA.isCompletedExceptionally()) {
System.out.println("task A " + taskA.join());
}
});
後面的執行緒使用的是ForkJoinPool,會建立相應的worker:tryAddWorker。裡面的程式碼真的是太底層了,unsafe class 會涉及很多
(4), 我們們再回頭簡單看看 DefaultFuture, FutureAdapter 繼承 completableFuture,futureAdapter中的 future.setCallback,會呼叫預設的DefaultFuture的setcallback。setCallback 會invoke callback:invokeCallback(callback);
其實dubbo 裡面的非同步支援比 2.6.x的非同步程式碼,改變很大,也很精妙。等有時間再說把,先看看CompletableFuture 強大的功能。
相關文章
- 《Java 8 in Action》Chapter 11:CompletableFuture:組合式非同步程式設計JavaAPT非同步程式設計
- 《Java8實戰》-第十一章筆記(CompletableFuture:組合式非同步程式設計)Java筆記非同步程式設計
- 非同步程式設計 CompletableFuture非同步程式設計
- 非同步程式設計利器:CompletableFuture非同步程式設計
- 計算機程式的思維邏輯 (94) – 組合式非同步程式設計計算機非同步程式設計
- 計算機程式的思維邏輯 (94) - 組合式非同步程式設計計算機非同步程式設計
- Java中的非同步程式設計與CompletableFuture應用Java非同步程式設計
- Java非同步程式設計:CompletableFuture與Future的對比Java非同步程式設計
- Java8新的非同步程式設計方式 CompletableFuture(二)Java非同步程式設計
- Java8新的非同步程式設計方式 CompletableFuture(三)Java非同步程式設計
- Java8新的非同步程式設計方式 CompletableFuture(一)Java非同步程式設計
- python 網路程式設計----非阻塞或非同步程式設計Python程式設計非同步
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- Java8 CompletableFuture 程式設計Java程式設計
- Socket程式設計中的同步、非同步、阻塞和非阻塞(轉)程式設計非同步
- 併發程式設計--常用方法以及CompletableFuture程式設計
- TypeScript 與組合式 APITypeScriptAPI
- 【進階之路】併發程式設計(三)-非阻塞同步機制程式設計
- ?【Java技術專區】「併發程式設計專題」教你如何使用非同步神器CompletableFutureJava程式設計非同步
- 實戰分析Java的非同步程式設計,並透過CompletableFuture進行高效調優Java非同步程式設計
- 非同步技巧之CompletableFuture非同步
- 非同步程式設計CompletableFuture實現高併發系統優化之請求合併非同步程式設計優化
- 一文徹底搞定(阻塞/非阻塞/同步/非同步)網路IO、併發程式設計模型、非同步程式設計模型的愛恨情仇非同步程式設計模型
- 程式設計師的“非程式設計師”之路程式設計師
- 非同步程式設計非同步程式設計
- Java 網路程式設計 —— 非阻塞式程式設計Java程式設計
- java併發程式設計學習15--CompletableFuture(二)Java程式設計
- 非同步程式設計:基於事件的非同步程式設計模式(EAP)非同步程式設計事件設計模式
- Dart 非同步程式設計Dart非同步程式設計
- php非同步程式設計PHP非同步程式設計
- asyncio 非同步程式設計非同步程式設計
- js 非同步程式設計JS非同步程式設計
- 非同步程式設計---Promise非同步程式設計Promise
- Javascript 非同步程式設計JavaScript非同步程式設計
- JavaScript非同步程式設計JavaScript非同步程式設計
- 使用非同步程式設計非同步程式設計
- java非同步程式設計Java非同步程式設計
- 《JAVA併發程式設計實戰》原子變數和非阻塞同步機制Java程式設計變數