我需要在多线程环境中在Scala中缓存一些东西。
阅读scalaz Memo
我在the code中找到了以下关于不可变哈希地图备忘录的评论:
由于此备忘录使用单个var,因此它是线程安全的。
代码如下所示:
def immutableMapMemo[K, V](m: Map[K, V]): Memo[K, V] = {
var a = m
memo[K, V](f =>
k => {
a get k getOrElse {
val v = f(k)
a = a updated (k, v)
v
}
})
}
说这是线程安全的,与我到目前为止所阅读和学到的关于JVM平台上的线程安全的内容相反;参考更新可能是原子的,但正如我所理解的那样,编译器可能会尝试进行某些优化,如果您没有内存障碍,则会破坏之前发生的关系。例如,请参阅this post和this。
但我确信那些scalaz人非常聪明。也许a
的范围有一些特别之处。
评论声称是真的,如果是,为什么?
答案 0 :(得分:5)
首先,由于var未标记为@volatile
,因此您可能会在不同的线程中看到a
的不同版本。所以你可能会在不同的线程上多次计算。这种方式会破坏记忆的目的,但除此之外它不会造成任何伤害,只要被记忆的功能没有副作用。
此外,在x86架构上,您几乎总能看到在所有其他线程上的一个线程上完成的更改。
关于地图的内部一致性:据我所知,在这种情况下,无法观察存储在不一致状态的地图,因为Map不仅仅是可观察的不可变的,但所有版本的Map(Map1,Map2,Map3,Map4,HashMap1,HashTrieMap,HashMapCollision1,EmptyMap)都只有最终字段,因此根据java内存模型是安全的。 然而,依赖于此非常脆弱。
例如,如果a
包含List或Vector,那么 能够在从不同线程快速更新它时以不一致的状态观察它。原因是这些数据结构是可观察的不可变的,但做在内部使用可变状态进行性能优化。
所以底线: 不 依赖于此在多线程环境中进行记忆。
有关非常类似问题的讨论,请参阅scala-user上的this thread
请参阅this thread,了解为什么即使使用@volatile
或其他安全机制(如actor)安全发布,也可以在不一致的状态下观察基本的可观察不可变数据结构(如List和Vector)。