異步計算
-
所謂異步調用其實就是實現一個可無需等待被調用函數的返回值而讓操作繼續運行的方法。在 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才合適,不然區別真的不大,還不如順序同步執行。