簡體   English   中英

`ArrayList::get` 線程安全嗎?

[英]Is `ArrayList::get` thread-safe?

我知道修改ArrayList使其不是線程安全的

➠ 但是如果ArrayList沒有被修改,也許是通過調用Collections.unmodifiableList來保護,調用ArrayList::get線程安全嗎?

例如,可以將ArrayList傳遞給Java Stream以對其元素進行並行處理嗎?

但是如果 ArrayList 沒有被修改是調用 ArrayList::get 線程安全嗎?

不,它不是線程安全的。

如果您執行以下操作,則會出現問題:

  1. 線程 A 創建並填充列表。
  2. 線程 A 將列表的引用傳遞給線程 B(關系之前沒有發生
  3. 線程 B 調用get在列表中。

除非在 1 和 3 之間的鏈之前發生適當的事件,否則線程 B 可能會看到陳舊的值……偶爾……在某些工作負載下的某些平台上。

有辦法解決這個問題。 例如,如果線程 A 在步驟 1 之后啟動線程 B,則之前會發生一次。 類似地,如果 A 通過正確同步的 setter / getter 調用或 volatile 變量將列表引用傳遞給 B,則會發生之前的情況。

但最重要的是,(只是)不更改列表不足以使其成為線程安全的。


... 可能通過調用 Collections.unmodifiableList 來保護

Collections.unmodifiableList的創建應該提供發生之前的關系......前提是您通過包裝器而不是直接通過ArrayList::get訪問列表。


例如,可以將 ArrayList 傳遞給 Java Stream 以對其元素進行並行處理嗎?

那是具體情況。 Stream 機制將提供發生之前的關系。 如果按預期使用。 這很復雜。

這來自Spliterator接口javadoc

“盡管它們在並行算法中具有明顯的效用,但預計拆分器不是線程安全的;相反,使用拆分器的並行算法實現應確保拆分器一次僅被一個線程使用。這通常很容易通過串行實現線程限制,這通常是通過遞歸分解工作的典型並行算法的自然結果。調用 trySplit() 的線程可能會將返回的 Spliterator 移交給另一個線程,后者又可能遍歷或進一步拆分該 Spliterator。的行為如果兩個或多個線程在同一個拆分器上並發操作,則拆分和遍歷是未定義的。如果原始線程將拆分器移交給另一個線程進行處理,那么最好在使用 tryAdvance() 消耗任何元素之前進行該移交,這是確定的保證(例如 SIZED 拆分器的估計尺寸()的准確性)僅在遍歷開始之前有效。”

換句話說,線程安全是 Spliterator 實現和 Stream 實現的共同責任。

考慮這一點的簡單方法是“魔法發生”……因為如果沒有,那么並行流將無法使用。

但請注意, Spliterator根本不需要使用ArrayList::get

線程安全只是一個問題,正如您所說的,線程之間的值何時可以更改。 如果沒有添加或刪除元素,則對象保持不變,所有線程都可以輕松對其進行操作。 這對於 Java 中的大多數對象都是一樣的。

您可以像這里看到的那樣跨線程添加到 ArrayList 中,但我不會相信它。

不, ArrayList.get()本質上不是線程安全的,因為它不修改List 你還需要一些東西來創建一個之前發生的每個之間關系get()修改列表中的每個方法調用。

然而,假設您首先實例化並填充列表,然后執行多個get() s,不再修改它,或者至少直到在所有get() s 之后的某個同步點之后。 然后,您不需要在各種get()之間進行相互同步,並且您可能能夠在get()和初始化階段結束之間獲得廉價的同步。 這實際上是您將其他非共享List作為並行流計算的輸入提供的情況。

暫無
暫無

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

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