Java 8並行流的效能陷阱
並行化流被分成多個塊,每個塊獨立處理,結果在最後彙總。
CPU密集型程式碼如下:
private long countPrimes(int max) { return range(1, max).parallel().filter(this::isPrime).count(); } private boolean isPrime(long n) { return n > 1 && rangeClosed(2, (long) sqrt(n)).noneMatch(divisor -> n % divisor == 0); } |
countPrimes 計算1到最大值之間的素數的數量。數字流由range方法建立,切換到並行模式,過濾掉非素數,剩餘的計算總數。由於isPrime 方法極其無效且佔用大量CPU,我們可以利用並行化並利用所有可用的CPU核心。
我們來看另一個例子:
private List<StockInfo> getStockInfo(Stream<String> symbols) { return symbols.parallel() .map(this::getStockInfo) //slow network operation .collect(toList()); } |
輸入是一個股票程式碼列表,我們必須呼叫慢速網路操作來獲取有關股票的一些細節。在這裡,我們不處理CPU密集型操作,但我們也可以利用並行化。並行執行多個網路請求是個好主意。同樣,並行流的一個很好的任務,你同意嗎?
如果您這樣做,請再次檢視上一個示例。有一個很大的錯誤。你看到了嗎?問題是所有並行流都使用公共fork-join執行緒池。如果提交長時間執行的任務,則會有效地阻塞池中的所有執行緒。因此,您將阻塞使用並行流的所有其他任務。
想象一下servlet環境,當一個請求呼叫時getStockInfo() ,另一個請求呼叫 countPrimes()。即使每個都需要不同的資源,也會阻止另一個。更糟糕的是,你不能為並行流指定執行緒池; 整個類載入器必須使用相同的。
讓我們在下面的例子中說明它:
private void run() throws InterruptedException { ExecutorService es = Executors.newCachedThreadPool(); // Simulating multiple threads in the system // if one of them is executing a long-running task. // Some of the other threads/tasks are waiting // for it to finish es.execute(() -> countPrimes(MAX, 1000)); //incorrect task es.execute(() -> countPrimes(MAX, 0)); es.execute(() -> countPrimes(MAX, 0)); es.execute(() -> countPrimes(MAX, 0)); es.execute(() -> countPrimes(MAX, 0)); es.execute(() -> countPrimes(MAX, 0)); es.shutdown(); es.awaitTermination(60, TimeUnit.SECONDS); } private void countPrimes(int max, int delay) { System.out.println( range(1, max).parallel() .filter(this::isPrime).peek(i -> sleep(delay)).count() ); } |
在這裡,我們模擬系統中的六個執行緒。所有這些都在執行CPU密集型任務,第一個被“暫停”,在它找到素數後就睡了一秒鐘。這只是一個人為的例子; 你可以想象一個被卡住或執行阻塞操作的執行緒。
問題是:執行此程式碼時會發生什麼?我們有六個任務; 其中一個將需要一整天才能完成,其餘的應該更快完成。毫不奇怪,每次執行程式碼時,都會得到不同的結果。你想在生產系統中有這樣的行為嗎?一個杜塞的任務取消了應用程式的其餘部分?我猜不會。
關於如何確保永遠不會發生這樣的事情,只有兩種選擇。第一個是確保提交到公共fork-join池的所有任務都不會卡,必須在合理的時間內完成。但這說起來容易做起來難,尤其是在複雜的應用程式中。
另一種選擇是不使用並行流,並等到Oracle允許我們指定用於並行流的執行緒池。
相關文章
- Java 8 Stream並行流Java並行
- Java8的新特性--並行流與序列流Java並行
- Java中的並行流處理與效能提升Java並行
- JDK8中的並行流JDK並行
- Java8中的流操作-基本使用&效能測試Java
- 強大的Stream並行流並行
- JAVA基礎之七-Collection和它的並行和流處理Java並行
- Java8——Stream流Java
- java 8 特性——stream流Java
- Java並行流誤區(學習筆記探討)Java並行筆記
- 《Java8實戰》-第七章筆記(並行資料處理與效能)Java筆記並行
- Stream並行流詳解並行
- JAVA拾遺 — JMH與8個測試陷阱Java
- Java8 Stream流的合併Java
- Java 8中HashMap的效能提升JavaHashMap
- Java 8 中 HashMap 的效能提升JavaHashMap
- java 8 Stream,Optional的流庫詳解Java
- 淺談java8中的流的使用Java
- java8新特性stream流Java
- spring boot使用Java並行流傳送kafka訊息報錯Spring BootJava並行Kafka
- Java List的remove()方法陷阱JavaREM
- Java 中的並行處理Java並行
- java8 Stream流操作介紹Java
- Java 8 流特性和 Lambda 表示式Java
- Java8的stream流讓操作集合更容易Java
- 8 個不得不說的 MySQL 陷阱MySql
- 並行和非並行在不通場景中的效能差異並行
- Java 中比較 BigDecimal 的陷阱JavaDecimal
- 提交訂單效能優化系列之006-普通的Thread多執行緒改為Java8的parallelStream併發流優化thread執行緒JavaParallel
- RangeBitmap提升Java流資料過濾效能Java
- [Java併發]執行緒的並行等待Java執行緒並行
- list轉map,使用java8,stream流Java
- Java 8 新特性:Stream 流快速入門Java
- JAVA + LR實現apache流媒體的效能測試JavaApache
- 並行取數提升報表效能並行
- Storm系列(四)並行度和流分組ORM並行
- 使用Speedment實現並行資料庫流並行資料庫
- Java並行流:一次搞定多執行緒程式設計難題,讓你的程式飛起來!Java並行執行緒程式設計