java 并行_在使用Java并行流之前要三思而后行
如果您聽Oracle的人談論Java
背后的設計選擇,您會經常聽到并行是主要動機。并行化是lambda,流API和其他背后的驅動力。讓我們看一下流API的示例。
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和最大值之間的質數。通過范圍方法創建數字流。然后將流切換到并行模式。不是質數的數字將被過濾掉,剩余的數字將被計數。
您會看到流API使我們能夠以簡潔明了的方式描述問題。而且,并行化只是調用該 parallel() 方法的問題。當我們這樣做時,流被分成多個塊,每個塊被獨立處理,并在最后匯總結果。由于我們對該isPrime 方法的實現 效率極低且占用大量CPU,因此我們可以利用并行化的優勢并利用所有可用的CPU內核。
讓我們看另一個例子:
private List getStockInfo(Stream 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
在這里,我們模擬系統中的六個線程。他們所有人都在執行CPU密集型任務,第一個被“破壞”并在找到質數后立即睡眠一秒鐘。這只是一個人為的例子。您可以想象一個線程被卡住或執行阻塞操作。
問題是:執行此代碼會發生什么?我們有六個任務;其中之一將需要一整天才能完成,其余的要早得多。毫不奇怪,每次執行代碼時,都會得到不同的結果。有時,所有健康的任務都會完成;其他時間,其中一些滯后于緩慢的時間。您是否希望在生產系統中有這種行為?一項破碎的任務使其他應用程序癱瘓了嗎?我猜不是。
如何確保這種事情永遠不會發生只有兩種選擇。首先是確保提交到公共fork-join池的所有任務不會卡住并在合理的時間內完成。但這說起來容易做起來難,尤其是在復雜的應用程序中。另一個選擇是不使用并行流,等到Oracle允許我們指定要用于并行流的線程池。
最后,開發這么多年我也總結了一套學習Java的資料與面試題,如果你在技術上面想提升自己的話,可以關注我,私信發送領取資料或者在評論區留下自己的聯系方式,有時間記得幫我點下轉發讓跟多的人看到哦。
總結
以上是生活随笔為你收集整理的java 并行_在使用Java并行流之前要三思而后行的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenJudge NOI 1.7 26
- 下一篇: java 聚合函数_如何使用Java流计