我對併發問題很陌生,因此對我毫無所知。我想創建一個使用Scala Map作爲後備存儲的線程安全容器。我寧願只公開它的方法的一個子集,而不是將用戶暴露給底層的Map。包裝一個不可變的集合並在Scala中維護線程安全?
例子可能看起來像下面...
class MyContainer[A] {
def add(thing: A): Unit = {
backingStore = backingStore + (thing.uuid -> thing)
}
def filter(p: A => Boolean): Option[Iterable[A]] = {
val filteredThings = backingStore.values.filter(p)
if (filteredThings.isEmpty) None else Some(filteredThings)
}
def remove(uuid: UUID): Option[A] = backingStore.get(uuid) match {
case optionalThing @ Some(thing) =>
backingStore = backingStore - uuid; optionalThing
case None => None
}
@ volatile private[this] var backingStore = immutable.HashMap.empty[UUID, A]
}
...我懷疑,即使底層的後備存儲是不變的,它的參考是volatile
,容器不是線程安全的。
假設我有兩個單獨的線程可以訪問上述容器的實例。線程1過濾底層集合並獲得一些結果;同時線程2刪除一個項目。線程1的結果可能包含對線程2刪除的項目的引用?可能還有其他問題。
我正確的說,上面的實現不是線程安全的嗎?使用Scala使上述線程安全的最習慣方法是什麼?
編輯:如果可能,我寧願避免阻塞和同步。如果必須使用阻塞/同步,那麼是否需要volatile參考?那不可改變的收藏的重點是什麼?難道我不能使用可變集合嗎?
@ladams如果我理解正確的話,我需要sychronize添加/刪除(即寫)方法;但對於像過濾器這樣的讀取方法,我不需要同步(除非我真的關心讀取和寫入的順序)。 – davidrpugh
@ladams如果我只同步'add'和'remove'方法,那麼我仍然需要將對後備存儲的引用註釋爲@volatile是否正確? – davidrpugh
是的,您需要同步添加/刪除方法。 將引用標記爲volatile有助於更新可見性的及時性:如果您不這樣做,讀者線程可能會在其他線程更新後保留對舊版本後備存儲的引用,時間不確定它可能會多次。 爲確保其他線程儘快可見,您需要做兩件事:讓寫入器從處理器緩存寫入內存(發生在同步塊的退出時),讓讀者將其讀入緩存中,軍隊。 – Iadams