簡體   English   中英

Java中的無限流並行處理

[英]parallel processing with infinite stream in Java

為什么下面的代碼不打印任何輸出,而如果我們刪除並行,它打印0,1?

IntStream.iterate(0, i -> ( i + 1 ) % 2)
         .parallel()
         .distinct()
         .limit(10)
         .forEach(System.out::println);

雖然我知道理想的限制應該放在不同之前,但我的問題更多地與添加並行處理引起的差異有關。

真正的原因是有序並行 .distinct()是完整的屏障操作, 文檔中所述:

保持並行管道中distinct()穩定性相對昂貴(要求操作充當完全屏障,具有大量緩沖開銷),並且通常不需要穩定性。

“全屏障操作”意味着必須在下游開始之前執行所有上游操作。 Stream API中只有兩個完整的屏障操作: .sorted() (每次)和.distinct() (按順序並行的情況)。 由於你有非短路無限流提供給.distinct()你最終會得到無限循環。 通過契約.distinct()不能以任何順序向下游發射元素:它應該總是發出第一個重復元素。 雖然理論上可以更好地實現並行有序.distinct() ,但實現起來要復雜得多。

至於解決方案,@ user140547是正確的:在.unordered()之前添加.unordered().distinct() distinct()算法切換為無序的( .distinct()使用共享的ConcurrentHashMap來存儲所有觀察到的元素並將每個新元素發送到下游)。 請注意,在.unordered() 之后添加.unordered() .distinct()將無濟於事。

Stream.iterate返回'無限順序有序流'。 因此,使順序流並行不是太有用。

根據Stream包的描述:

對於並行流,放寬排序約束有時可以實現更高效的執行。 如果元素的排序不相關,則可以更有效地實現某些聚合操作,例如過濾重復(distinct())或分組縮減(Collectors.groupingBy())。 類似地,與遇到訂單本質上相關的操作(例如limit())可能需要緩沖以確保正確排序,從而破壞並行性的好處。 在流具有遭遇順序但用戶不特別關心該遭遇順序的情況下,使用無序()明確地對流進行排序可以改善某些有狀態或終端操作的並行性能。 然而,大多數流管道,例如上面的“塊的權重總和”示例,即使在排序約束下仍然有效地並行化。

在您的情況下似乎就是這種情況,使用無序(),它打印0,1。

    IntStream.iterate(0, i -> (i + 1) % 2)
            .parallel()
            .unordered()
            .distinct()
            .limit(10)
            .forEach(System.out::println);

我知道代碼不正確,並且正如解決方案中所建議的那樣,如果我們在不同之前移動限制,我們就不會有無限循環。

並行函數使用fork和join概念來分配工作,它為工作分配所有可用的線程,而不是單個線程。

我們正確地期待無限循環,因為多線程無限地處理數據並且沒有什么能阻止它們,因為10的限制永遠不會在不同之后發生。

它可能會繼續嘗試fork並且永遠不會嘗試加入以向前移動它。 但我仍然相信它在java中的缺陷比其他任何東西都要多。

這個代碼有一個主要問題,即使沒有並行:在.distinct()之后,流只有2個元素 - 因此限制永遠不會啟動 - 它將打印兩個元素,然后繼續浪費你的CPU時間無限期。 這可能是你想要的。

在平行和限制的情況下,我認為由於工作分工的方式,問題更加嚴重。 我沒有完全跟蹤並行流代碼,但這是我的猜測:

並行代碼在多個線程之間划分工作,這些線程都無限期地突然顯示,因為它們從未填充其配額。 系統可能會等待每個線程完成,因此它可以將它們的結果組合起來以確保順序的清晰度 - 但是在您提供的情況下這絕不會發生。

如果沒有訂單要求,則可以在針對全局清晰度集進行檢查后立即使用每個工作線程的結果。

沒有限制,我懷疑使用不同的代碼來處理無限流:而不是等待所需的10填充,結果報告為已發現。 它有點像制作報告hasNext()= true的迭代器,首先產生0,然后是1,然后next()調用永久掛起而不產生結果 - 在並行情況下,某些東西正在等待多個報告,因此它可以正確組合/在輸出之前對它們進行排序,而在串行的情況下,它會執行它可以掛起的內容。

我試着在有和沒有distinct()或limit()的情況下找到調用堆棧的確切差異,但到目前為止,導航相當復雜的流庫調用序列似乎非常困難。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM