第二部分:併發工具類24->CompletableFuture,非同步程式設計沒那麼難
1.非同步程式設計
序列操作並行化,涉及到非同步化
非同步化,是並行方案得以實施的接觸,利用多執行緒優化效能這個核心方案來實施的基礎。
jdk1.8提供了CompletableFuture支援非同步程式設計
2.CompletableFuture 核心優勢
燒水泡茶,3個任務
任務1,洗水壺,燒開水
任務2,洗茶壺,洗茶杯,拿茶葉
任務3,泡茶
任務3需要等待任務1,和任務2都完成才可以執行
有一些沒有見過的方法,
runAsync()
supplyAsync()
thenCombine()
特點:
1.無需手工維護執行緒,沒有手工維護執行緒的工作,給任務分配執行緒不需要我們關注
2.語意清晰,f3 = f1.thenCombine(f2,() -> {})能夠清晰表述“任務3等待任務1和任務2都完成後才開始”
3.程式碼簡練,專注業務邏輯
//任務1:洗水壺->燒開水 CompletableFuture<Void> f1 = CompletableFuture.runAsync(()->{ System.out.println("T1:洗水壺..."); sleep(1, TimeUnit.SECONDS); System.out.println("T1:燒開水..."); sleep(15, TimeUnit.SECONDS); }); //任務2:洗茶壺->洗茶杯->拿茶葉 CompletableFuture<String> f2 = CompletableFuture.supplyAsync(()->{ System.out.println("T2:洗茶壺..."); sleep(1, TimeUnit.SECONDS); System.out.println("T2:洗茶杯..."); sleep(2, TimeUnit.SECONDS); System.out.println("T2:拿茶葉..."); sleep(1, TimeUnit.SECONDS); return "龍井"; }); //任務3:任務1和任務2完成後執行:泡茶 CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tf)->{ System.out.println("T1:拿到茶葉:" + tf); System.out.println("T1:泡茶..."); return "上茶:" + tf; }); //等待任務3執行結果 System.out.println(f3.join()); void sleep(int t, TimeUnit u) { try { u.sleep(t); }catch(InterruptedException e){} } // 一次執行結果: T1:洗水壺... T2:洗茶壺... T1:燒開水... T2:洗茶杯... T2:拿茶葉... T1:拿到茶葉:龍井 T1:泡茶... 上茶:龍井
3.CompletableFuture物件
建立
//使用預設執行緒池 static CompletableFuture<Void> runAsync(Runnable runnable) static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) //可以指定執行緒池 static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync(Runnable runnable)
supplyAsync(Supplier supplier)
區別是什麼?Runnable介面的run方法沒有返回值,Supplier的get方法是有返回值的,其餘額外的是帶執行緒池傳遞
預設不傳的話用的是預設ForkJoinPool執行緒池,執行緒數預設是cpu核數
建議根據不同業務型別建立不同的執行緒池,以免相互干擾
注意事項:
建立完CompletableFuture物件之後,自動非同步執行runnable.run()方法或者supplier.get()方法
這也可以通過future來獲取執行結果和判斷執行緒是否執行結束。
4.CompletableFuture的高階功能
CompletableFuture類實現了CompletionStage介面,有40多個方法
CompletionStage介面
大概說明:
序列關係
並行關係
匯聚關係
異常處理
前面提到的f3 = f1.thenCombine(f2, () -> {})描述的就是匯聚關係,說白了就是AND聚合關係
f3是等f1,f2都執行完,才執行;當然還是or關係,只有其中1個執行完就可以執行
4.1 序列關係
thenApply,thenAccept,thenRun,thenCompose四個介面
CompletionStage<R> thenApply(fn);
CompletionStage<R> thenApplyAsync(fn);
CompletionStage<Void> thenAccept(consumer);
CompletionStage<Void> thenAcceptAsync(consumer);
CompletionStage<Void> thenRun(action);
CompletionStage<Void> thenRunAsync(action);
CompletionStage<R> thenCompose(fn);
CompletionStage<R> thenComposeAsync(fn);
4.2 and 匯聚關係
CompletionStage applyToEither(other, fn);
CompletionStage applyToEitherAsync(other, fn);
CompletionStage acceptEither(other, consumer);
CompletionStage acceptEitherAsync(other, consumer);
CompletionStage runAfterEither(other, action);
CompletionStage runAfterEitherAsync(other, action);
CompletionStage<R> thenCombine(other, fn);
CompletionStage<R> thenCombineAsync(other, fn);
CompletionStage<Void> thenAcceptBoth(other, consumer);
CompletionStage<Void> thenAcceptBothAsync(other, consumer);
CompletionStage<Void> runAfterBoth(other, action);
CompletionStage<Void> runAfterBothAsync(other, action);
4.3 or匯聚關係
CompletionStage applyToEither(other, fn);
CompletionStage applyToEitherAsync(other, fn);
CompletionStage acceptEither(other, consumer);
CompletionStage acceptEitherAsync(other, consumer);
CompletionStage runAfterEither(other, action);
CompletionStage runAfterEitherAsync(other, action);
4.4 異常處理
CompletableFuture<Integer>
f0 = CompletableFuture.
.supplyAsync(()->(7/0))
.thenApply(r->r*10);
System.out.println(f0.join());
正常是try-catch,函數語言程式設計中,更簡單,提供了鏈式處理異常
CompletionStage exceptionally(fn);
CompletionStage<R> whenComplete(consumer);
CompletionStage<R> whenCompleteAsync(consumer);
CompletionStage<R> handle(fn);
CompletionStage<R> handleAsync(fn);
異常範例
CompletableFuture f0 = CompletableFuture .supplyAsync(()->(7/0)) .thenApply(r->r*10) .exceptionally(e->0);System.out.println(f0.join());
原創:做時間的朋友