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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java 8 - 正确高效的使用并行流

發(fā)布時(shí)間:2025/3/21 java 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 8 - 正确高效的使用并行流 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • Pre
  • 正確使用并行流,避免共享可變狀態(tài)
  • 高效使用并行流
  • 流的數(shù)據(jù)源和可分解性


Pre

Java 8 - 并行流計(jì)算入門


正確使用并行流,避免共享可變狀態(tài)

錯(cuò)用并行流而產(chǎn)生錯(cuò)誤的首要原因,就是使用的算法改變了某些共享狀態(tài)。下面是另一種實(shí)現(xiàn)對(duì)前n個(gè)自然數(shù)求和的方法,但這會(huì)改變一個(gè)共享累加器:

public static long sideEffectSum(long n) {Accumulator accumulator = new Accumulator();LongStream.rangeClosed(1, n).forEach(accumulator::add);return accumulator.total; } public class Accumulator {public long total = 0;public void add(long value) { total += value; } }

有什么問題呢?

它在本質(zhì)上就是順序的。每次訪問 total 都會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)。如果用同步來修復(fù),那就完全失去并行的意義了。

為了說明這一點(diǎn),讓我們?cè)囍?Stream 變成并行的:

public static long sideEffectParallelSum(long n) {Accumulator accumulator = new Accumulator();LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);return accumulator.total; }

測(cè)試下,輸出


性能無關(guān)緊要了,唯一要緊的是每次執(zhí)行都會(huì)返回不同的結(jié)果,都離正確值差很遠(yuǎn)。這是由于多個(gè)線程在同時(shí)訪問累加器,執(zhí)行 total += value ,而這卻不是一個(gè)原子操作。問題的根源在于, forEach 中調(diào)用的方法有副作用它會(huì)改變多個(gè)線程共享的對(duì)象的可變狀態(tài)。

要是你想用并行 Stream 又不想引發(fā)類似的意外,就必須避免這種情況。

所以共享可變狀態(tài)會(huì)影響并行流以及并行計(jì)算,要避免共享可變狀態(tài),確保并行 Stream 得到正確的結(jié)果。


高效使用并行流

是否有必要使用并行流?

  • 如果有疑問,多次測(cè)試結(jié)果。把順序流轉(zhuǎn)成并行流輕而易舉,但卻不一定是好事
  • 留意裝箱。自動(dòng)裝箱和拆箱操作會(huì)大大降低性能

Java 8中有原始類型流( IntStream 、LongStream 、 DoubleStream )來避免這種操作,但?有可能都應(yīng)該用這些流。

  • 有些操作本身在并行流上的性能就比順序流差。特別是 limit 和 findFirst 等依賴于元素順序的操作,它們?cè)诓⑿辛魃蠄?zhí)行的代價(jià)非常大。

例如, findAny 會(huì)比 findFirst 性能好,因?yàn)樗灰欢ㄒ错樞騺韴?zhí)行。可以調(diào)用 unordered 方法來把有序流變成無序流。那么,如果你需要流中的n個(gè)元素而不是專門要前n個(gè)的話,對(duì)無序并行流調(diào)用limit 可能會(huì)比單個(gè)有序流(比如數(shù)據(jù)源是一個(gè) List )更高效。

  • 還要考慮流的操作流水線的總計(jì)算成本。

設(shè)N是要處理的元素的總數(shù),Q是一個(gè)元素通過流水線的大致處理成本,則N*Q就是這個(gè)對(duì)成本的一個(gè)粗略的定性估計(jì)。Q值較高就意味著使用并行流時(shí)性能好的可能性比較大。

  • 對(duì)于較小的數(shù)據(jù)量,選擇并行流幾乎從來都不是一個(gè)好的決定。并行處理少數(shù)幾個(gè)元素的好處還?不上并行化造成的額外開銷

  • 要考慮流背后的數(shù)據(jù)結(jié)構(gòu)是否易于分解。

例如, ArrayList 的拆分效率比 LinkedList高得多,因?yàn)榍罢哂貌恢闅v就可以平均拆分,而后者則必須遍歷。
另外,用 range 工廠方法創(chuàng)建的原始類型流也可以快速分解。

  • 流自身的特點(diǎn),以及流水線中的中間操作修改流的方式,都可能會(huì)改變分解過程的性能。

例如,一個(gè) SIZED 流可以分成大小相等的兩部分,這樣每個(gè)部分都可以比較高效地并行處理,但篩選操作可能丟棄的元素個(gè)數(shù)卻無法預(yù)測(cè),導(dǎo)致流本身的大小未知。

  • 還要考慮終端操作中合并步驟的代價(jià)是大是小(例如 Collector 中的 combiner 方法)

如果這一步代價(jià)很大,那么組合每個(gè)子流產(chǎn)生的部分結(jié)果所付出的代價(jià)就可能會(huì)超出通過并行流得到的性能提升。


流的數(shù)據(jù)源和可分解性

最后, 并行流背后使用的基礎(chǔ)架構(gòu)是Java 7中引入的分支/合并框架了解它的內(nèi)部原理至關(guān)重要,下一篇搞起

總結(jié)

以上是生活随笔為你收集整理的Java 8 - 正确高效的使用并行流的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。