非同步計算
-
所謂非同步呼叫其實就是實現一個可無需等待被呼叫函式的返回值而讓操作繼續執行的方法。在 Java 語言中,簡單的講就是另啟一個執行緒來完成呼叫中的部分計算,使呼叫繼續執行或返回,而不需要等待計算結果。但呼叫者仍需要取執行緒的計算結果。
-
JDK5新增了Future介面,用於描述一個非同步計算的結果。雖然 Future 以及相關使用方法提供了非同步執行任務的能力,但是對於結果的獲取卻是很不方便,只能通過阻塞或者輪詢的方式得到任務的結果。阻塞的方式顯然和我們的非同步程式設計的初衷相違背,輪詢的方式又會耗費無謂的 CPU 資源,而且也不能及時地得到計算結果。
- 以前我們獲取一個非同步任務的結果可能是這樣寫的
Future 介面的侷限性
Future介面可以構建非同步應用,但依然有其侷限性。它很難直接表述多個Future 結果之間的依賴性。實際開發中,我們經常需要達成以下目的:
-
將多個非同步計算的結果合併成一個
-
等待Future集合中的所有任務都完成
-
Future完成事件(即,任務完成以後觸發執行動作)
- 。。。
函數語言程式設計
CompletionStage
-
CompletionStage代表非同步計算過程中的某一個階段,一個階段完成以後可能會觸發另外一個階段
-
一個階段的計算執行可以是一個Function,Consumer或者Runnable。比如:stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println())
-
一個階段的執行可能是被單個階段的完成觸發,也可能是由多個階段一起觸發
CompletableFuture
- 在Java8中,CompletableFuture提供了非常強大的Future的擴充套件功能,可以幫助我們簡化非同步程式設計的複雜性,並且提供了函數語言程式設計的能力,可以通過回撥的方式處理計算結果,也提供了轉換和組合 CompletableFuture 的方法。
- 它可能代表一個明確完成的Future,也有可能代表一個完成階段( CompletionStage ),它支援在計算完成以後觸發一些函式或執行某些動作。
- 它實現了Future和CompletionStage介面
CompletableFuture基本用法
建立CompletableFuture
thenApply
當前階段正常完成以後執行,而且當前階段的執行的結果會作為下一階段的輸入引數。thenApplyAsync預設是非同步執行的。這裡所謂的非同步指的是不在當前執行緒內執行。
thenApply相當於回撥函式(callback)
thenAccept與thenRun
- 可以看到,thenAccept和thenRun都是無返回值的。如果說thenApply是不停的輸入輸出的進行生產,那麼thenAccept和thenRun就是在進行消耗。它們是整個計算的最後兩個階段。
-
同樣是執行指定的動作,同樣是消耗,二者也有區別:
-
thenAccept接收上一階段的輸出作為本階段的輸入
- thenRun根本不關心前一階段的輸出,根本不不關心前一階段的計算結果,因為它不需要輸入引數
-
thenCombine整合兩個計算結果
例如,此階段與其它階段一起完成,進而觸發下一階段:
whenComplete
最後,舉個栗子:
事實上,如果每個操作都很簡單的話(比如:上面的例子中按照id去查)沒有必要用這種多執行緒非同步的方式,因為建立執行緒還需要時間,還不如直接同步執行來得快。
事實證明,只有當每個操作很複雜需要花費相對很長的時間(比如,呼叫多個其它的系統的介面;比如,商品詳情頁面這種需要從多個系統中查資料顯示的)的時候用CompletableFuture才合適,不然區別真的不大,還不如順序同步執行。