面試官:java8中parallelStream提升數倍查詢效率是怎樣實現的

2020-09-18 不合格的CRUD程式設計師

業務場景

在很多項目中,都有類似數據匯總的業務場景,查詢今日註冊會員數,在線會員數,訂單總金額,支出總金額等。。。這些業務通常都不是存在同一張表中,我們需要依次查詢出來然後封裝成所需要的對象返回給前端。那麼在此過程中,就可以把這個接口中「大任務」拆分成N個小任務,異步執行這些小任務,等到最後一個小任務執行完,把所有任務的執行結果封裝到返回結果中,統一返回到前端展示。

同步執行

首先看看同步執行的代碼

public class Test { @Data @NoArgsConstructor @AllArgsConstructor @ToString class Result { /** * 在線人數 */ Integer onlineUser; /** * 註冊人數 */ Integer registered; /** * 訂單總額 */ BigDecimal orderAmount; /** * 支出總額 */ BigDecimal outlayAmount; } @org.junit.Test public void collect() { System.out.println(&34;); long startTime = System.currentTimeMillis(); Integer onlineUser = queryOnlineUser(); Integer registered = queryRegistered(); BigDecimal orderAmount = queryOrderAmount(); BigDecimal outlayAmount = queryOutlayAmount(); Result result = new Result(onlineUser, registered, orderAmount, outlayAmount); long endTime = System.currentTimeMillis(); System.out.println(&34; + result); System.out.println(&34; + (endTime - startTime) + &34;); } public Integer queryOnlineUser() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(&34;); return 10; } public Integer queryRegistered() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(&34;); return 10086; } public BigDecimal queryOrderAmount() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(&34;); return BigDecimal.valueOf(2000); } public BigDecimal queryOutlayAmount() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(&34;); return BigDecimal.valueOf(1000); }} 執行時長想必大家都能夠想得到,理所應當是10秒以上數據匯總開始查詢在線人數 耗時2秒查詢註冊人數 耗時2秒查詢訂單總額 耗時3秒查詢支出總額 耗時3秒獲取匯總數據結束,result = Test.Result(onlineUser=10, registered=10086, orderAmount=2000, outlayAmount=1000)總耗時 = 10008毫秒

異步執行

下面換成異步執行,用java8的parallelStream(並行流),這裡為什麼不用Thread呢,這裡有一個注意點,我們需要獲取所有所有子任務執行完的時間點,在這個時間點之後才能將結果封裝返回,Thread沒有辦法滿足,這裡parallelStream和函數式接口就登場了。

java8的特性之一 —— lambda表達式,就是配合函數式接口使用的。

java8內置了四大核心函數式接口:

1、Consumer : 消費型接口 void accept(T t);

2、Supplier : 供給型接口 T get();

3、Function<T,R> : 函數型接口 R apply(T t);

4、Predicate : 斷言型接口 boolean test(T t);

這四大核心函數式接口其下還有很多子接口,基本上能滿足日常項目所用,這裡扯遠了。。 直接上代碼。

這裡我們需要使用的是Runable接口,是無參無返回值的一個接口。在實際場景中,可能有時間範圍之類的查詢參數的,則可以根據不同業務使用不同的接口。這種方式也可以用Future接口去實現,有興趣的可以試一試,這裡就不多做敘述了。

@org.junit.Testpublic void collect() { System.out.println(&34;); long startTime = System.currentTimeMillis(); Result result = new Result(); List<Runnable> taskList = new ArrayList<Runnable>() { { add(() -> result.setOnlineUser(queryOnlineUser())); add(() -> result.setRegistered(queryRegistered())); add(() -> result.setOrderAmount(queryOrderAmount())); add(() -> result.setOutlayAmount(queryOutlayAmount())); } }; taskList.parallelStream().forEach(v -> v.run()); long endTime = System.currentTimeMillis(); System.out.println(&34; + result); System.out.println(&34; + (endTime - startTime) + &34;);}

執行結果,由於四個子任務都是並行的,效率直接提升了三倍,如果子任務越多的話提升效果越明顯。

數據匯總開始查詢在線人數 耗時2秒查詢註冊人數 耗時2秒查詢訂單總額 耗時3秒查詢支出總額 耗時3秒獲取匯總數據結束,result = Test.Result(onlineUser=10, registered=10086, orderAmount=2000, outlayAmount=1000)總耗時 = 3079毫秒

總結

1.parallelStream是異步編程的好幫手,在使用過程中一定要注意線程安全的問題。

2.以上這種方式只能用在沒有事務的業務中,因為在多線程中,事務是不共享的。

相關焦點

  • JAVA lambda操作一定要擁抱並行流parallelStream
    業務場景在很多項目中,都有類似數據匯總的業務場景,查詢今日註冊會員數,在線會員數,訂單總金額,支出總金額等……這些業務通常都不是存在同一張表中,我們需要依次查詢出來然後封裝成所需要的對象返回給前端。同步執行首先看看同步執行的代碼執行時長想必大家都能夠想得到,理所應當是10秒以上異步執行下面換成異步執行,用java8的parallelStream(並行流),這裡為什麼不用Thread呢,這裡有一個注意點
  • 巧用Java8中的Stream,讓集合操作飛起來!
    【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫作者:堅持就是勝利juejin.im/post/5d5e2616f265da03b638b28a簡介java8
  • 「JAVA8」- Stream
    Stream 使用一種類似用 SQL 語句從資料庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。Stream API可以極大提高Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的代碼。
  • java8中的findAny與findFirst
    java8中開啟了流式編程。今天我們來聊聊流失編程中的findAny和findFirst。findFirst是從流中找出第一個元素。而findAny則是從流中找出任意一個元素。是這樣嗎?我們來實際使用驗證一下吧。兩個方法中一樣的集合,分別調用執行了findAny和findFirst。為了驗證執行的結果,對每個方法都做了10次循環。讓我們來看看執行後輸出的結果是什麼。怎麼輸出的結果一樣呢?
  • ParallelStream的那些坑
    換成parallelStream,會發生什麼情況?在執行的邏輯中,我們讓每個任務都sleep 1秒鐘,這樣就能夠模擬一些I/O請求的耗時等待。我是很沮喪的發現,很多高級研發,將線程池的各種參數背的滾瓜爛熟,各種調優,竟然敢睜一隻眼閉一隻眼的在I/O密集型業務中用上parallelStream。
  • for 循環中的並行流parallelStream
    如何優化這塊性能,想到了最近看到的jdk8 的parallelStream ,考慮試下是否講原來的串行換成並行,是否可以達到性能提升的目的。考慮到也是剛剛接觸parallelStream 相關的代碼,先實現了簡單的例子,確認for 循環的parallelStream 實現方法。
  • java8的這些編程技巧,錯過了真的會後悔
    ().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));就可以實現上面testCount1中多行代碼的邏輯。
  • java8新特性-Stream與LocalDateTime
    JAVA8的Streamstream現在是越用越多了,還是不是太熟練,簡單記錄下,經常使用的是map與filtermap 簡單數就是處理,方法用於映射每個元素到對應的結果,以下代碼片段使用 map 輸出了元素對應的平方數:List<Integer
  • 採用Java8中lambda表達式 實現List操作
    *;import static java.util.stream.Collectors.toList;/** * @author admin */public class MyTest {    /**     * 
  • Java8中你可能不知道的一些地方之Stream實戰
    流的概念Java Se中對於流的操作有輸入輸出IO流,而Java8中引入的Stream 屬於Java API中的一個新成員,它允許你以聲明性方式處理數據集合,Stream 使用一種類似 SQL 語句從資料庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。 注意這裡的流操作可以看做是對集合數據的處理。
  • 並行流ParallelStream的陷阱你知道嗎
    ParallelStream底層使用了Fork/Join框架實現,也就是應用了線程池ForkJoinPool把並行流中的節點抽象為多核機器中,使用ParallelStream在流的節點中的所有操作都相當於在「一個多線程環境中」進行操作,裡面的所有操作都會產生不可預期的結果
  • 以面試的角度學習hashmap原理(面試官從淺到深必問的map八大問題)
    本人以面試官角度整理hashmap原理,以達到「目的性」學習1.hashmap簡介(如下,數組-鍊表形式)HashMap的存儲結構圖中,紫色部分即代表哈希表,hashmap也稱為哈希數組(默認數組大小是16,每對key-value鍵值對其實是存在map的內部類entry裡的),數組的每個元素都是一個單鍊表的頭節點,跟著的綠色鍊表是用來解決衝突的
  • Java 8 Stream 閃亮登場
     ===> 1.897     *  parallelStreamForeach 循環耗時 ===> 1.942     *  parallelStreamForeach 循環耗時 
  • 樂字節Java8核心特性實戰之五:Stream(流)
    屬於Java API中的一個新成員,它允許你以聲明性方式處理數據集合,Stream 使用一種類似 SQL 語句從資料庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。,例如從資料庫中查詢用戶記錄 按用戶id 查詢 降序排列 然後通過list 接收用戶記錄,數據的計算已在放入集合前完成。
  • parallelStream的那些坑,不踩不知道,一踩嚇一跳
    .mapToInt(Widget::getWeight) .sum();這段代碼有一個關鍵的函數,那就是stream。問題來了假如我們把stream換成parallelStream,會發生什麼情況?根據字面上的意思,流會從串行 變成並行。
  • Java 16都出來了,你還不會Java 8 Stream?
    Stream API 藉助於同樣新出現的 Lambda 表達式,極大的提高編程效率和程序可讀性。同時它提供串行和並行兩種模式進行匯聚操作,併發模式能夠充分利用多核處理器的優勢。"b","c"};Stream<String> parallel = Arrays.stream(arrays).parallel();使用方法與非並行流無異String[] arrays = {"a","b","c"};Stream<String
  • Java 8 Stream
    Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。在 Java 8 中, 集合接口有兩個方法來生成流:stream() − 為集合創建串行流。parallelStream() − 為集合創建並行流。
  • 集合高階用法-(Stream)流操作
    stream 特性Stream(流)是一個來自數據源的元素隊列並支持聚合操作,它使用一種類似用 SQL 語句從資料庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。元素是特定類型的對象,形成一個隊列。 Java中的Stream並不會存儲元素,而是按需計算。
  • Java8新特性Stream
    從結果看,兩種篩選的方式都達到了目的,我們對比defaultOption和streamOption的實現過程可以發現。streamOption中使用了Stream並配合Lambda表達式進行操作,書寫更為簡潔。
  • Java8用了這麼久了,你確定Stream流用法及語法都知道嗎?
    用來處理集合,通過 使用Stream API 對集合數據進行操作,就類似於使用 SQL 執行的資料庫查詢,Stream API 提供了一種高效且易於使用的處理數據的方式為什麼用Java 8 Stream ?因為 操作簡單為什麼操作簡單?因為 Lambda 表達式,它極大的提高了編程效率和程序可讀性怎麼操作流?