<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
寫多執行緒程式的時候,可以使用Future從一個非同步執行緒中拿到結果,但是如果使用過程中會發現一些問題:
為了解決Future問題,JDK在1.8的時候給我們提供了一個好用的工具類CompletableFuture;
它實現了Future和CompletionStage介面,針對Future的不足之處給出了相應的處理方式。
CompletableFuture的核心思想是將每個非同步任務都可以看做一個步驟(CompletionStage),然後其他的非同步任務可以根據這個步驟做一些想做的事情。
CompletionStage定義了許多步驟處理的方法,功能非常強大,這裡就只列一下日常中常用到的一些方法供大家參考。
簡單的使用方式
非同步執行,無需結果:
// 可以執行Executors非同步執行,如果不指定,預設使用ForkJoinPool CompletableFuture.runAsync(() -> System.out.println("Hello CompletableFuture!"));
非同步執行,同時返回結果:
// 同樣可以指定執行緒池 CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> "Hello CompletableFuture!"); System.out.println(stringCompletableFuture.get());
thenRun: 不需要上一步的結果,直接直接新的操作
thenAccept:獲取上一步非同步處理的內容,進行新的操作
thenApply: 獲取上一步的內容,然後產生新的內容
所有加上Async字尾的,代表新的處理操作仍然是非同步的。Async的操作都可以指定Executors進行處理
// Demo CompletableFuture .supplyAsync(() -> "Hello CompletableFuture!") // 針對上一步的結果做處理,產生新的結果 .thenApplyAsync(s -> s.toUpperCase()) // 針對上一步的結果做處理,不返回結果 .thenAcceptAsync(s -> System.out.println(s)) // 不需要上一步返回的結果,直接進行操作 .thenRunAsync(() -> System.out.println("end")); ;
當我們有兩個回撥在處理的時候,任何完成都可以使用,兩者結果沒有關係,那麼使用acceptEither。
兩個非同步執行緒誰先執行完成,用誰的結果,其餘型別的方法也是如此。
// 返回abc CompletableFuture .supplyAsync(() -> { SleepUtils.sleep(100); return "Hello CompletableFuture!"; }) .acceptEither(CompletableFuture.supplyAsync(() -> "abc"), new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }); // 返回Hello CompletableFuture! CompletableFuture .supplyAsync(() -> "Hello CompletableFuture!") .acceptEither(CompletableFuture.supplyAsync(() -> { SleepUtils.sleep(100); return "abc"; }), new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } });
thenCombine
當我們有兩個CompletionStage時,需要對兩個的結果進行整合處理,然後計算得出一個新的結果。
CompletableFuture<Integer> heightAsync = CompletableFuture.supplyAsync(() -> 172); CompletableFuture<Double> weightAsync = CompletableFuture.supplyAsync(() -> 65) .thenCombine(heightAsync, new BiFunction<Integer, Integer, Double>() { @Override public Double apply(Integer wight, Integer height) { return wight * 10000.0 / (height * height); } }) ;
thenAcceptBoth
需要兩個非同步CompletableFuture的結果,兩者都完成的時候,才進入thenAcceptBoth回撥。
// thenAcceptBoth案例: CompletableFuture .supplyAsync(() -> "Hello CompletableFuture!") .thenAcceptBoth(CompletableFuture.supplyAsync(() -> "abc"), new BiConsumer<String, String>() { // 引數一為我們剛開始執行時的CompletableStage,新傳入的作為第二個引數 @Override public void accept(String s, String s2) { System.out.println("param1=" + s + ", param2=" + s2); } }); // 結果:param1=Hello CompletableFuture!, param2=abc
當我們使用CompleteFuture進行鏈式呼叫的時候,多個非同步回撥中,如果有一個執行出現問題,那麼接下來的回撥都會停止,所以需要一種例外處理策略。
exceptionally
exceptionally是當出現錯誤時,給我們機會進行恢復,自定義返回內容。
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("發生錯誤"); }).exceptionally(throwable -> { log.error("呼叫錯誤 {}", throwable.getMessage(), throwable); return "例外處理內容"; });
handle
exceptionally是隻有發生異常時才會執行,而handle則是不管是否發生錯誤都會執行。
CompletableFuture.supplyAsync(() -> { return "abc"; }) .handle((r,err) -> { log.error("呼叫錯誤 {}", err.getMessage(), err); // 對結果做額外的處理 return r; }) ;
需求為對某個表中特定條件的使用者進行簡訊通知,但是簡訊使用者有成百上千萬,如果使用單執行緒讀取效率會很慢。這個時候可以考慮使用多執行緒的方式進行讀取;
1、將讀取任務拆分為多個不同的子任務,指定讀取的偏移量和個數
// 假設有500萬條記錄 long recordCount = 500 * 10000; int subTaskRecordCount = 10000; // 對記錄進行分片 List<Map> subTaskList = new LinkedList<>(); for (int i = 0; i < recordCount / 500; i++) { // 如果子任務結構複雜,建議使用物件 HashMap<String, Integer> subTask = new HashMap<>(); subTask.put("index", i); subTask.put("offset", i * subTaskRecordCount); subTask.put("count", subTaskRecordCount); subTaskList.add(subTask); }
2、使用多執行緒進行批次讀取
// 進行subTask批次處理,拆分為不同的任務 subTaskList.stream() .map(subTask -> CompletableFuture.runAsync(()->{ // 讀取資料,然後處理 // dataTunel.read(subTask); },excuturs)) // 使用應用的通用任務執行緒池 .map(c -> ((CompletableFuture<?>) c).join());
3、進行業務邏輯處理,或者直接在讀取完進行業務邏輯處理也是可以;
在系統拆分比較細的時候,價格,優惠券,庫存,商品詳情等資訊分散在不同的系統中,有時候需要同時獲取商品的所有資訊, 有時候可能只需要獲取商品的部分資訊。
當然問題點在於要呼叫多個不同的系統,需要將RT降低下來,那麼需要進行並行呼叫;
List<Task> taskList = new ArrayList<>(); List<Object> result = taskList.stream() .map(task -> CompletableFuture.supplyAsync(()->{ // handlerMap.get(task).query(); return ""; }, executorService)) .map(c -> c.join()) .collect(Collectors.toList());
exceptionally是隻有發生異常時才會執行,而handle則是不管是否發生錯誤都會執行。
一般情況下上述簡單的API已經滿足絕大部分的場景了,如果有更復雜的訴求,可繼續深入研究。
到此這篇關於Java多執行緒工具CompletableFuture的使用教學的文章就介紹到這了,更多相關Java CompletableFuture內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45