01-CompletableFuture非同步執行緒 入門
前言
emmm, 有好長一段時間沒有釋出新的文章了,最後一篇釋出還是1月21日,到現在已經快3個月了,這段時間我去幹嘛了呢?
1: 學習資料結構與演算法, 但是還沒有學完,打算等學習完畢後再給大家分享
2: 學習Java 9 - 15的新特性, 應為關注Java的發展方向這一塊來說,基本是每個學習Java的人員所必備的, 為啥沒有16和17呢?應為我在網上沒有找到好的視訊,所以等以後找到了在學習而且新特特性應該不是很多,但是9-17加起來就很多了
3: 就是最近這段時間我離職了, 在交接和找新的工作,所很大一部分精力都用在了這個上面
但是今天給大家帶來了一個新的東西,這個東西用的人也比較少, 其實也不是很新,是Java8中的一個類而已,但是非常好用, 用過多執行緒的人應該都知道,執行緒的建立,回收,管理,池化,都很難弄, 但是學習了這個東西, 媽媽再也不用擔心我用不好多執行緒了
CompletableFuture是什麼
從名稱看來和Future有關,沒錯,他也是Future的實現,和FutureTask平級,也是用來實現非同步執行緒任務的,並且攜帶返回值, 具體的使用直接從需求出發,關注下面的需求和實現, 即可掌握
需求
小白來餐廳吃飯, 點了一盤番茄炒蛋+米飯,小白開始打王者,廚師開始炒菜,小白開吃
需求點: 廚師需要單獨的執行緒
實現
編寫程式碼
package com.dance; import org.junit.jupiter.api.Test; import java.util.StringJoiner; import java.util.concurrent.CompletableFuture;public class CompletableFutureTest { @Test public void testOne(){ print("小白進入餐廳"); print("小白點了 番茄炒蛋 + 一碗米飯"); CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> { print("廚師炒菜"); sleep(200); print("廚師打飯"); sleep(100); return "番茄炒蛋 + 米飯 好了"; }); print("小白在打王者"); print(String.format("%s , 小白開吃", cf1.join())); } /** * 休眠方法 * @param millis 毫秒 */ public static void sleep(long millis){ try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 列印方法 * @param text 文字 */ public static void print(String text){ String str = new StringJoiner("\t|\t") .add(String.valueOf(System.currentTimeMillis())) .add(String.valueOf(Thread.currentThread().getId())) .add(Thread.currentThread().getName()) .add(text) .toString(); System.out.println(str); } }
執行結果
1649430128924 | 1 | main | 小白進入餐廳 1649430128924 | 1 | main | 小白點了 番茄炒蛋 + 一碗米飯 1649430128926 | 1 | main | 小白在打王者 1649430128927 | 24 | ForkJoinPool.commonPool-worker-19 | 廚師炒菜 1649430129134 | 24 | ForkJoinPool.commonPool-worker-19 | 廚師打飯 1649430129244 | 1 | main | 番茄炒蛋 + 米飯 好了 , 小白開吃
廚師用單獨的執行緒去幹活了, 非同步執行緒,如此簡單
需求進化
在餐廳中一般廚師都只負責炒菜,像打飯這樣的事情都是交給服務員來的
需求點:廚師炒完菜後交給服務員,服務員新開執行緒去打飯
實現
編寫程式碼
@Test public void testTwo(){ print("小白進入餐廳"); print("小白點了 番茄炒蛋 + 一碗米飯"); CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> { print("廚師炒菜"); sleep(200); return "番茄炒蛋"; }).thenCompose(dis -> CompletableFuture.supplyAsync(() -> { print("服務員打飯"); sleep(100); return dis + " + 米飯 好了"; })); print("小白在打王者"); print(String.format("%s , 小白開吃", cf1.join())); }
執行結果
1649431094323 | 1 | main | 小白進入餐廳 1649431094324 | 1 | main | 小白點了 番茄炒蛋 + 一碗米飯 1649431094326 | 1 | main | 小白在打王者 1649431094326 | 24 | ForkJoinPool.commonPool-worker-19 | 廚師炒菜 1649431094538 | 24 | ForkJoinPool.commonPool-worker-19 | 服務員打飯 1649431094645 | 1 | main | 番茄炒蛋 + 米飯 好了 , 小白開吃
按道理來說這裡應該是兩個執行緒的,但是估計我這個執行的太快了,所以後面的任務也提交給了這個執行緒,我感覺這種程式碼呼叫流程就很清晰,看著像Promise
需求進化
小白進入餐廳的時候,開始點菜,要一盤番茄炒蛋+米飯, 但是這個時候米飯是沒有蒸好的,需要開始去蒸,所以廚師炒菜,服務員去蒸飯,這兩個事情應該是同時進行的,在廚師炒完菜,服務員蒸好飯,廚師將菜交給服務員,服務員打飯,交給小白,小白吃飯
需求點: 廚師炒菜和服務員蒸飯需要同時進行,並且是廚師炒完菜交給服務員
實現
編寫程式碼
@Test public void testThree(){ print("小白進入餐廳"); print("小白點了 番茄炒蛋 + 一碗米飯"); CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> { print("廚師炒菜"); sleep(200); return "番茄炒蛋"; }).thenCombine(CompletableFuture.supplyAsync(() -> { print("服務員開始蒸飯"); sleep(300); return "米飯"; }), (dis, rice) -> { print("服務員打飯"); sleep(100); return String.format("%s + %s , 好了", dis, rice); }); print("小白在打王者"); print(String.format("%s , 小白開吃", cf1.join())); }
執行結果
1649431885256 | 1 | main | 小白進入餐廳 1649431885256 | 1 | main | 小白點了 番茄炒蛋 + 一碗米飯 1649431885259 | 1 | main | 小白在打王者 1649431885259 | 24 | ForkJoinPool.commonPool-worker-19 | 服務員開始蒸飯 1649431885259 | 25 | ForkJoinPool.commonPool-worker-5 | 廚師炒菜 1649431885571 | 24 | ForkJoinPool.commonPool-worker-19 | 服務員打飯 1649431885681 | 1 | main | 番茄炒蛋 + 米飯 , 好了 , 小白開吃
這裡出現了兩個執行緒,正好可以看到
總結
方法名 |
描述 |
supplyAsync |
用來開啟一個非同步任務 |
thenCompose |
用來連線兩個有依賴關係的任務,結果由最後一個返回 |
thenCombine |
用來合併兩個任務,結果由函式(BiFunction)返回 |
第一個任務的需求點在於執行緒的開啟
第二個任務的需求點在於兩個執行緒的連線
第三個任務的需求點在於兩個執行緒的結果合併
怎麼樣到這裡是不是已經簡單的入門了呢
作者:彼岸舞
時間:2022\04\11
內容關於:CompeletableFuture
本文來源於網路,只做技術分享,一概不負任何責任