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

时间:2015-11-12 12:46:40

标签: scala hashmap thread-safety scalaz memoization

我需要在多线程环境中在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 postthis

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

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

1 个答案:

答案 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)。