日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java8 - 避免代码阻塞的骚操作

發布時間:2025/3/21 java 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java8 - 避免代码阻塞的骚操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • Pre
  • 避免同步阻塞的困擾
    • V1.0 改進 -采用Stream 順序查詢 (不理想)
    • V2.0 改進 - 使用并行流對請求進行并行操作 (good)
    • V3.0 改進 - 使用 CompletableFuture發起異步請求 ()
  • 更好的方案


Pre

Java8 - 使用工廠方法 supplyAsync創建 CompletableFuture

接著上面的例子

假設非常不幸,無法控制 Shop 類提供API的具體實現,最終提供給你的API都是同步阻塞式的方法。這也是當你試圖使用服務提供的HTTP API時最常發生的情況。你會學到如何以異步的方式查詢多個商店,避免被單一的請求所阻塞,并由此提升你的“最佳價格查詢器”的性能和吞吐量。


避免同步阻塞的困擾

假設你需要查詢的所有商店只提供了同步API,換句話說,你有一個商家的列表,如下所示:

List<Shop> shops = Arrays.asList(new Shop("BestPrice"),new Shop("LetsSaveBig"),new Shop("MyFavoriteShop"),new Shop("BuyItAll"));

你需要使用下面這樣的簽名實現一個方法,它接受產品名作為參數,返回一個字符串列表,這個字符串列表中包括商店的名稱、該商店中指定商品的價格:

public List<String> findPrices(String product);

V1.0 改進 -采用Stream 順序查詢 (不理想)

第一個想法可能是使用 Stream 特性。

【采用順序查詢所有商店的方式實現的 findPrices 方法】

public List<String> findPrices(String product) {return shops.stream().map(shop -> String.format("%s price is %.2f",shop.getName(), shop.getPrice(product))).collect(toList()); }

好吧,這段代碼看起來非常直白。 此外,也請記錄下方法的執行時間,通過這
些數據,我們可以比較優化之后的方法會帶來多大的性能提升,具體的代碼清單如下。

【驗證 findPrices 的正確性和執行性能】

long start = System.nanoTime(); System.out.println(findPrices("myPhone27S")); long duration = (System.nanoTime() - start) / 1_000_000; System.out.println("Done in " + duration + " msecs");

輸出

[BestPrice price is 123.26, LetsSaveBig price is 169.47, MyFavoriteShop price is 214.13, BuyItAll price is 184.74] Done in 4032 msecs

正如你預期的, findPrices 方法的執行時間4S+,因為對這4個商店的查詢是順序進行的,并且一個查詢操作會阻塞另一個,每一個操作都要花費大于1S的時間計算請求商品的價格。

怎樣才能改進這個結果呢?


V2.0 改進 - 使用并行流對請求進行并行操作 (good)

對V1.0改成并行試試?

【對 findPrices 進行并行操作】

public List<String> findPrices(String product) { return shops.parallelStream().map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product))).collect(toList()); }

區別在于 parallelStream ,使用并行流并行流從不同的商店獲取價格。

運行代碼,與V·1.0的執行結果相比較,發現了新版 findPrices 的改進了吧。

[BestPrice price is 123.26, LetsSaveBig price is 169.47, MyFavoriteShop price is 214.13, BuyItAll price is 184.74] Done in 1180 msecs

相當不錯啊!看起來這是個簡單但有效的主意:現在對四個不同商店的查詢實現了并行,所以完成所有操作的總耗時只有1S多一點兒。

還能能做得更好嗎? 要不試試CompletableFuture ,將 findPrices 方法中對不同商店的同步調用替換為異步調用。


V3.0 改進 - 使用 CompletableFuture發起異步請求 ()

我們可以使用工廠方法 supplyAsync 創建 CompletableFuture 對象。讓我們把它利用起來:

List<CompletableFuture<String>> priceFutures =shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> String.format("%s price is %.2f",shop.getName(), shop.getPrice(product)))).collect(toList());

使用這種方式,你會得到一個 List<CompletableFuture<String>> ,列表中的每個CompletableFuture 對象在計算完成后都包含商店的 String 類型的名稱。但是,由于你用CompletableFutures 實現的 findPrices 方法要求返回一個 List<String> ,你需要等待所有的 future 執行完畢,將其包含的值抽取出來,填充到列表中才能返回

為了實現這個效果,你可以向最初的 List<CompletableFuture<String>> 添加第二個map 操作,對 List 中的所有 future 對象執行 join 操作,一個接一個地等待它們運行結束。

Note: CompletableFuture 類中的 join 方法和 Future 接口中的 get 有相同的含義,并且也聲明在Future 接口中,它們唯一的不同是 join 不會拋出任何檢測到的異常。使用它你不再需要使用try / catch 語句塊讓你傳遞給第二個 map 方法的Lambda表達式變得過于臃腫。

所有這些整合在一起,你就可以重新實現 findPrices 了,具體代碼如下

public List<String> findPrices(String product) {List<CompletableFuture<String>> priceFutures =shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " +shop.getPrice(product))).collect(Collectors.toList()); return priceFutures.stream().map(CompletableFuture::join).collect(toList()); }

注意到了嗎?這里使用了兩個不同的 Stream 流水線,而不是在同一個處理流的流水線上一個接一個地放兩個 map 操作——這其實是有緣由的。

考慮流操作之間的延遲特性,如果你在單一流水線中處理流,發向不同商家的請求只能以同步、順序執行的方式才會成功。因此,每個創建 CompletableFuture 對象只能在前一個操作結束之后執行查詢指定商家的動作、通知 join方法返回計算結果。

【為什么 Stream 的延遲特性會引起順序執行,以及如何避免】見下圖


上半部分展示了使用單一流水線處理流的過程,我們看到,執行的流程(以虛線標識)是順序的。事實上,新的 CompletableFuture 對象只有在前一個操作完全結束之后,才能創建。與此相反,圖的下半部分展示了如何先將 CompletableFutures 對象聚集到一個列表中(即圖中以橢圓表示的部分),讓對象們可以在等待其他對象完成操作之前就能啟動。

運行代碼 第三個版本 findPrices 方法的性能,你會得到下面這幾行輸出:

[BestPrice price is 123.26, LetsSaveBig price is 169.47, MyFavoriteShop price is 214.13, BuyItAll price is 184.74] Done in 2005 msecs


超過2S意味著利用 CompletableFuture 實現的版本比剛開始原生順序執行且會發生阻塞的版本快。但是它的用時也差不多是使用并行流的前一個版本的兩倍。尤其是,考慮到從順序執行的版本轉換到并行流的版本只做了非常小的改動,就讓人更加沮喪

與此形成鮮明對比的是,我們為采用 CompletableFutures 完成的新版方法做了大量的工作!

但,這就是全部的真相嗎?這種場景下使用 CompletableFutures 真的是浪費時間嗎?或者我們可能漏了某些重要的東西?


更好的方案

并行流的版本工作得非常好,那是因為它能并行地執行四個任務,所以它幾乎能為每個商家分配一個線程。但是,如果你想要增加第五個商家到商點列表中,讓你的“最佳價格查詢”應用

總結

以上是生活随笔為你收集整理的Java8 - 避免代码阻塞的骚操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。