🏆【Java技術之旅】教你如何使用異步神器CompletableFuture

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"前提概要","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在java8以前,我們使用java的多線程編程,一般是通過Runnable中的run方法來完成,這種方式,有個很明顯的缺點,就是,沒有返回值。這時候,大家可能會去嘗試使用Callable中的call方法,然後用Future返回結果,如下:","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) throws Exception {\n ExecutorService executor = Executors.newSingleThreadExecutor();\n Future stringFuture = executor.submit(new Callable() {\n @Override\n public String call() throws Exception {\n Thread.sleep(2000);\n return \"async thread\";\n }\n });\n Thread.sleep(1000);\n System.out.println(\"main thread\");\n System.out.println(stringFuture.get());\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過觀察控制檯,我們發現先打印 main thread ,一秒後打印 async thread,似乎能滿足我們的需求。但仔細想我們發現一個問題,當調用future的get()方法時,當前主線程是堵塞的,這好像並不是我們想看到的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一種獲取返回結果的方式是先輪詢,可以調用isDone,等完成再獲取,但這也不能讓我們滿意.","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"很多個異步線程執行時間可能不一致,我的主線程業務不能一直等着,這時候我可能會想要只等最快的線程執行完或者最重要的那個任務執行完,亦或者我只等1秒鐘,至於沒返回結果的線程我就用默認值代替.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"我兩個異步任務之間執行獨立,但是第二個依賴第一個的執行結果.","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"java8的CompletableFuture,就在這混亂且不完美的多線程江湖中閃亮登場了.CompletableFuture讓Future的功能和使用場景得到極大的完善和擴展,提供了函數式編程能力,使代碼更加美觀優雅,而且可以通過回調的方式計算處理結果,對異常處理也有了更好的處理手段.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CompletableFuture源碼中有四個靜態方法用來執行異步任務:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"創建任務","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static CompletableFuture supplyAsync(Supplier supplier){..}\npublic static CompletableFuture supplyAsync(Supplier supplier,Executor executor){..}\npublic static CompletableFuture runAsync(Runnable runnable){..}\npublic static CompletableFuture runAsync(Runnable runnable,Executor executor){..} \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果有多線程的基礎知識,我們很容易看出,run開頭的兩個方法,用於執行沒有返回值的任務,因爲它的入參是Runnable對象。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而supply開頭的方法顯然是執行有返回值的任務了,至於方法的入參,如果沒有傳入Executor對象將會使用ForkJoinPool.commonPool() 作爲它的線程池執行異步代碼.在實際使用中,一般我們使用自己創建的線程池對象來作爲參數傳入使用,這樣速度會快些.","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"執行異步任務的方式也很簡單,只需要使用上述方法就可以了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture future = CompletableFuture.supplyAsync(() -> {\n    //....執行任務\n    return \"hello\";\n}, executor)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來看一下獲取執行結果的幾個方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"V get();\nV get(long timeout,Timeout unit);\nT getNow(T defaultValue);\nT join();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"上面兩個方法是Future中的實現方式,get()會堵塞當前的線程,這就造成了一個問題,如果執行線程遲遲沒有返回數據,get()會一直等待下去,因此,第二個get()方法可以設置等待的時間.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"getNow()方法比較有意思,表示當有了返回結果時會返回結果,如果異步線程拋了異常會返回自己設置的默認值.","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來以一些場景的實例來介紹一下CompletableFuture中其他一些常用的方法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"thenAccept()\npublic CompletionStage thenAccept(Consumer super T> action);\npublic CompletionStage thenAcceptAsync(Consumer super T> action);\npublic CompletionStage thenAcceptAsync(Consumer super T> action,Executor executor);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:當前任務正常完成以後執行,當前任務的執行結果可以作爲下一任務的輸入參數,無返回值.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:執行任務A,同時異步執行任務B,待任務B正常返回後,B的返回值執行任務C,任務C無返回值","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.supplyAsync(() -> \"任務A\");\nCompletableFuture futureB = CompletableFuture.supplyAsync(() -> \"任務B\");\nCompletableFuture futureC = futureB.thenApply(b -> {\n System.out.println(\"執行任務C.\");\n System.out.println(\"參數:\" + b);//參數:任務B\n return \"a\";\n});\nthenRun(..)\npublic CompletionStage thenRun(Runnable action);\npublic CompletionStage thenRunAsync(Runnable action);\npublic CompletionStage thenRunAsync(Runnable action,Executor executor);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:對不關心上一步的計算結果,執行下一個操作","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:執行任務A,任務A執行完以後,執行任務B,任務B不接受任務A的返回值(不管A有沒有返回值),也無返回值","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.supplyAsync(() -> \"任務A\");\nfutureA.thenRun(() -> System.out.println(\"執行任務B\"));\nthenApply(..)\npublic CompletableFuture thenApply(Function super T,? extends U> fn)\npublic CompletableFuture thenApplyAsync(Function super T,? extends U> fn)\npublic CompletableFuture thenApplyAsync(Function super T,? extends U> fn, Executor executor)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:當前任務正常完成以後執行,當前任務的執行的結果會作爲下一任務的輸入參數,有返回值","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:多個任務串聯執行,下一個任務的執行依賴上一個任務的結果,每個任務都有輸入和輸出","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"異步執行任務A,當任務A完成時使用A的返回結果resultA作爲入參進行任務B的處理,可實現任意多個任務的串聯執行","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.supplyAsync(() -> \"hello\");\nCompletableFuture futureB = futureA.thenApply(s->s + \" world\");\nCompletableFuture future3 = futureB.thenApply(String::toUpperCase);\nSystem.out.println(future3.join());\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的代碼,我們當然可以先調用future.join()先得到任務A的返回值,然後再拿返回值做入參去執行任務B,而thenApply的存在就在於幫我簡化了這一步,我們不必因爲等待一個計算完成而一直阻塞着調用線程,而是告訴CompletableFuture你啥時候執行完就啥時候進行下一步. 就把多個任務串聯起來了.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"thenCombine(..) thenAcceptBoth(..) runAfterBoth(..)\npublic CompletableFuture thenCombine(CompletionStage extends U> other, BiFunction super T,? super U,? extends V> fn)\npublic CompletableFuture thenCombineAsync(CompletionStage extends U> other, BiFunction super T,? super U,? extends V> fn)\npublic CompletableFuture thenCombineAsync(CompletionStage extends U> other, BiFunction super T,? super U,? extends V> fn, Executor executor)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:結合兩個CompletionStage的結果,進行轉化後返回","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:需要根據商品id查詢商品的當前價格,分兩步,查詢商品的原始價格和折扣,這兩個查詢相互獨立,當都查出來的時候用原始價格乘折扣,算出當前價格. 使用方法:thenCombine(..)","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" CompletableFuture futurePrice = CompletableFuture.supplyAsync(() -> 100d);\n CompletableFuture futureDiscount = CompletableFuture.supplyAsync(() -> 0.8);\n CompletableFuture futureResult = futurePrice.thenCombine(futureDiscount, (price, discount) -> price * discount);\n System.out.println(\"最終價格爲:\" + futureResult.join()); //最終價格爲:80.0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"thenCombine(..)是結合兩個任務的返回值進行轉化後再返回,那如果不需要返回呢,那就需要","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"thenAcceptBoth(..),同理,如果連兩個任務的返回值也不關心呢,那就需要runAfterBoth了,如果理解了上面三個方法,thenApply,thenAccept,thenRun,這裏就不需要單獨再提這兩個方法了,只在這裏提一下.","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"thenCompose(..)\npublic CompletableFuture thenCompose(Function super T,? extends CompletionStage> fn)\npublic CompletableFuture thenComposeAsync(Function super T,? extends CompletionStage> fn)\npublic CompletableFuture thenComposeAsync(Function super T,? extends CompletionStage> fn, Executor executor)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:這個方法接收的輸入是當前的CompletableFuture的計算值,返回結果將是一個新的CompletableFuture","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個方法和thenApply非常像,都是接受上一個任務的結果作爲入參,執行自己的操作,然後返回.那具體有什麼區別呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"thenApply():它的功能相當於將CompletableFuture轉換成CompletableFuture,改變的是同一個CompletableFuture中的泛型類型","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"thenCompose():用來連接兩個CompletableFuture,返回值是一個新的CompletableFuture","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.supplyAsync(() -> \"hello\");\nCompletableFuture futureB = futureA.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + \"world\"));\nCompletableFuture future3 = futureB.thenCompose(s -> CompletableFuture.supplyAsync(s::toUpperCase));\nSystem.out.println(future3.join());\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"applyToEither(..) acceptEither(..) runAfterEither(..)\npublic CompletionStage applyToEither(CompletionStage extends T> other,Function super T, U> fn);\npublic CompletionStage applyToEitherAsync(CompletionStage extends T> other,Function super T, U> fn);\npublic CompletionStage applyToEitherAsync(CompletionStage extends T> other,Function super T, U> fn,Executor executor);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:執行兩個CompletionStage的結果,那個先執行完了,就是用哪個的返回值進行下一步操作場景:假設查詢商品a,有兩種方式,A和B,但是A和B的執行速度不一樣,我們希望哪個先返回就用那個的返回值.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {\n try {\n Thread.sleep(1000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n return \"通過方式A獲取商品a\";\n });\nCompletableFuture futureB = CompletableFuture.supplyAsync(() -> {\n try {\n Thread.sleep(2000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n return \"通過方式B獲取商品a\";\n });\n\nCompletableFuture futureC = futureA.applyToEither(futureB, product -> \"結果:\" + product);\nSystem.out.println(futureC.join()); //結果:通過方式A獲取商品a\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"同樣的道理,applyToEither的兄弟方法還有acceptEither(),runAfterEither(),我想不需要我解釋你也知道該怎麼用了.","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"exceptionally(..)\npublic CompletionStage exceptionally(Function fn);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:當運行出現異常時,調用該方法可進行一些補償操作,如設置默認值.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:異步執行任務A獲取結果,如果任務A執行過程中拋出異常,則使用默認值100返回.","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.\n supplyAsync(() -> \"執行結果:\" + (100 / 0))\n .thenApply(s -> \"futureA result:\" + s)\n .exceptionally(e -> {\n System.out.println(e.getMessage()); //java.lang.ArithmeticException: / by zero\n return \"futureA result: 100\";\n });\nCompletableFuture futureB = CompletableFuture.\n supplyAsync(() -> \"執行結果:\" + 50)\n .thenApply(s -> \"futureB result:\" + s)\n .exceptionally(e -> \"futureB result: 100\");\nSystem.out.println(futureA.join());//futureA result: 100\nSystem.out.println(futureB.join());//futureB result:執行結果:50\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面代碼展示了正常流程和出現異常的情況,可以理解成catch,根據返回值可以體會下.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"whenComplete(..)\npublic CompletionStage whenComplete(BiConsumer super T, ? super Throwable> action);\npublic CompletionStage whenCompleteAsync(BiConsumer super T, ? super Throwable> action);\npublic CompletionStage whenCompleteAsync(BiConsumer super T, ? super Throwable> action,Executor executor);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能:當CompletableFuture的計算結果完成,或者拋出異常的時候,都可以進入whenComplete方法執行,舉個栗子","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture futureA = CompletableFuture.\n supplyAsync(() -> \"執行結果:\" + (100 / 0))\n .thenApply(s -> \"apply result:\" + s)\n .whenComplete((s, e) -> {\n if (s != null) {\n System.out.println(s);//未執行\n }\n if (e == null) {\n System.out.println(s);//未執行\n } else {\n System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero\n }\n })\n .exceptionally(e -> {\n System.out.println(\"ex\"+e.getMessage()); //ex:java.lang.ArithmeticException: / by zero\n            return \"futureA result: 100\"; }); \nSystem.out.println(futureA.join());//futureA result: 100\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據控制檯,我們可以看出執行流程是這樣,supplyAsync->whenComplete->exceptionally,可以看出並沒有進入thenApply執行,原因也顯而易見,在supplyAsync中出現了異常,thenApply只有當正常返回時纔會去執行.而whenComplete不管是否正常執行,還要注意一點,whenComplete是沒有返回值的.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面代碼我們使用了函數式的編程風格並且先調用whenComplete再調用exceptionally,如果我們先調用exceptionally,再調用whenComplete會發生什麼呢,我們看一下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"複製代碼CompletableFuture futureA = CompletableFuture.supplyAsync(() -> \"執行結果:\" + (100 / 0)).thenApply(s -> \"apply result:\" + s).exceptionally(e -> {System.out.println(\"ex:\"+e.getMessage()); //ex:java.lang.ArithmeticException: / by zeroreturn \"futureA result: 100\";}).whenComplete((s, e) -> {if (e == null) {System.out.println(s);//futureA result: 100} else {System.out.println(e.getMessage());//未執行}});System.out.println(futureA.join());//futureA result: 100","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼先執行了exceptionally後執行whenComplete,可以發現,由於在exceptionally中對異常進行了處理,並返回了默認值,whenComplete中接收到的結果是一個正常的結果,被exceptionally美化過的結果,這一點需要留意一下.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"handle(..)\npublic CompletionStage handle(BiFunction super T, Throwable, ? extends U> fn);\npublic CompletionStage handleAsync(BiFunction super T, Throwable, ? extends U> fn);\npublic CompletionStage handleAsync(BiFunction super T, Throwable, ? extends U> fn,Executor executor);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能:當CompletableFuture的計算結果完成,或者拋出異常的時候,可以通過handle方法對結果進行處理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" CompletableFuture futureA = CompletableFuture.\n supplyAsync(() -> \"執行結果:\" + (100 / 0))\n .thenApply(s -> \"apply result:\" + s)\n .exceptionally(e -> {\n System.out.println(\"ex:\" + e.getMessage()); //java.lang.ArithmeticException: / by zero\n return \"futureA result: 100\";\n })\n .handle((s, e) -> {\n if (e == null) {\n System.out.println(s);//futureA result: 100\n } else {\n System.out.println(e.getMessage());//未執行\n }\n return \"handle result:\" + (s == null ? \"500\" : s);\n });\nSystem.out.println(futureA.join());//handle result:futureA result: 100\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過控制檯,我們可以看出,最後打印的是handle result:futureA result: 100,執行exceptionally後對異常進行了\"美化\",返回了默認值,那麼handle得到的就是一個正常的返回,我們再試下,先調用handle再調用exceptionally的情況.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" CompletableFuture futureA = CompletableFuture.\n supplyAsync(() -> \"執行結果:\" + (100 / 0))\n .thenApply(s -> \"apply result:\" + s)\n .handle((s, e) -> {\n if (e == null) {\n System.out.println(s);//未執行\n } else {\n System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero\n }\n return \"handle result:\" + (s == null ? \"500\" : s);\n })\n .exceptionally(e -> {\n System.out.println(\"ex:\" + e.getMessage()); //未執行\n return \"futureA result: 100\";\n });\nSystem.out.println(futureA.join());//handle result:500\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據控制檯輸出,可以看到先執行handle,打印了異常信息,並對接過設置了默認值500,exceptionally並沒有執行,因爲它得到的是handle返回給它的值,由此我們大概推測handle和whenComplete的區別","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"都是對結果進行處理,handle有返回值,whenComplete沒有返回值","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"由於1的存在,使得handle多了一個特性,可在handle裏實現exceptionally的功能","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"allOf(..) anyOf(..)\npublic static CompletableFuture allOf(CompletableFuture>... cfs)\npublic static CompletableFuture anyOf(CompletableFuture>... cfs)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"allOf:當所有的CompletableFuture都執行完後執行計算","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"anyOf:最快的那個CompletableFuture執行完之後執行計算","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景二:查詢一個商品詳情,需要分別去查商品信息,賣家信息,庫存信息,訂單信息等,這些查詢相互獨立,在不同的服務上,假設每個查詢都需要一到兩秒鐘,要求總體查詢時間小於2秒.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) throws Exception {\n\n ExecutorService executorService = Executors.newFixedThreadPool(4);\n\n long start = System.currentTimeMillis();\n CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {\n try {\n Thread.sleep(1000 + RandomUtils.nextInt(1000));\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n return \"商品詳情\";\n },executorService);\n\n CompletableFuture futureB = CompletableFuture.supplyAsync(() -> {\n try {\n Thread.sleep(1000 + RandomUtils.nextInt(1000));\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n return \"賣家信息\";\n },executorService);\n\n CompletableFuture futureC = CompletableFuture.supplyAsync(() -> {\n try {\n Thread.sleep(1000 + RandomUtils.nextInt(1000));\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n return \"庫存信息\";\n },executorService);\n\n CompletableFuture futureD = CompletableFuture.supplyAsync(() -> {\n try {\n Thread.sleep(1000 + RandomUtils.nextInt(1000));\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n return \"訂單信息\";\n },executorService);\n\n CompletableFuture allFuture = CompletableFuture.allOf(futureA, futureB, futureC, futureD);\n allFuture.join();\n\n System.out.println(futureA.join() + futureB.join() + futureC.join() + futureD.join());\n System.out.println(\"總耗時:\" + (System.currentTimeMillis() - start));\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章