是一个只更新Scala中单个var线程安全的类实例吗?

Eri*_*sen 2 scala hashmap memoization thread-safety scalaz

我需要在多线程环境中在Scala中缓存一些东西.

阅读scalaz,Memo我在不可变哈希映射备忘录的代码中找到了以下注释:

由于此备忘录使用单个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
        }
      })
  }
Run Code Online (Sandbox Code Playgroud)

说这是线程安全的,与我到目前为止所阅读和学到的关于JVM平台上的线程安全的内容相反; 参考更新可能是原子的,但正如我所理解的那样,如果您没有内存屏障,编译器可能会尝试进行某些优化来扰乱发生在之前的关系.例如,请参阅此帖此内容.

但我确信那些scalaz人非常聪明.也许有关于范围的特别之处a.

评论声称是真的,如果是,为什么?

Rüd*_*ehn 5

首先,由于未标记var @volatile,您可能会a在不同的线程中看到不同的版本.所以你可能会在不同的线程上多次计算.这种方式违背了记忆的目的,但除此之外它不会造成任何伤害,只要被记忆的功能没有副作用.

此外,在x86架构上,您几乎总能看到在所有其他线程上的一个线程上完成的更改.

关于地图的内部一致性:据我所知,在这种情况下,不可能观察存储在不一致状态的地图,因为Map不仅是可观察的不可变的,而是所有版本的Map(Map1,Map2, Map3,Map4,HashMap1,HashTrieMap,HashMapCollision1,EmptyMap)只有最终字段,因此根据java内存模型是安全的.但是,依赖于此非常脆弱.

例如,如果a包含List或Vector,则在从不同线程快速更新时,您能够以不一致的状态观察它.这样做的原因是这些数据结构是可观察的不可变的,但内部使用可变状态进行性能优化.

所以底线:不要依赖于这个在多线程上下文中的memoization.

有关非常类似问题的讨论,请参阅scala-user上的此主题

请参阅此主题,了解为什么即使使用安全发布@volatile或其他安全机制(如actor),也可以在不一致的状态下观察基本的可观察不可变数据结构(如List和Vector).