【问题标题】:Scala immutable collections cannot be shared without synchronization?Scala不可变集合不能在没有同步的情况下共享?
【发布时间】:2016-01-16 21:55:14
【问题描述】:

来自《在 Scala 中学习并发编程》一书:

然而,在当前版本的 Scala (2.11.1) 中,某些集合是 被认为是不可变的,例如 List 和 Vector,不能共享 同步。尽管他们的外部 API 不允许您 修改它们,它们包含非最终字段。

谁能用一个小例子来证明这一点?这仍然适用于 2.11.7 吗?

【问题讨论】:

标签: scala concurrency


【解决方案1】:

从另一个线程查看时在一个线程中所做的更改的行为由Java Memory Model 控制。特别是,当涉及到构建一个集合,然后将构建的并且现在不可变的集合传递给另一个线程时,这些规则非常弱。 JMM 不保证其他线程不会看到未完全构建集合的早期视图!

由于synchronized 块强制执行排序,因此如果在每个操作中使用它们,它们可用于获得一致的视图。

但在实践中,这很少是真正需要的。在 CPU 端,通常有一个 memory barrier 操作可用于强制内存一致性(即,如果您写入列表的尾部,然后通过内存屏障,则没有其他线程可以看到尾部未设置)。在实践中,JVM 通常必须通过使用内存屏障来实现同步。因此,人们可能希望您可以在 synchronzied 块中传递创建的列表,相信会发出内存屏障,之后一切都会好起来的。

不幸的是,JMM 并不要求它以这种方式实现(并且您不能假设对象创建的类似内存屏障的行为实际上是一个完整的内存屏障,适用于该线程中的所有内容而不是简单的该对象的最终字段),这就是为什么推荐是它的原因,以及为什么它在库中没有固定(无论如何)。

对于它的价值,在 x86 架构上,如果您在 synchronized 块中传递不可变对象,我从未观察到问题。如果您尝试使用 CAS(例如,通过使用 java.util.concurrent.atomic 类),我发现了一些问题。

【讨论】:

  • 至少使用java.util.concurrent.atomic.AtomicReference应该是安全的,因为原子访问和更新的记忆效应通常遵循易失性的规则[docs](原子访问和更新的记忆效应通常遵循挥发物的规则),对吧?
  • @RüdigerKlaehn - 您可以使用内存屏障和比较和交换来实现类似的效果,但是 AFAIK 原子类尽可能使用 CAS,因为它们通常性能更好。因此,虽然我没有来自 AtomicReference 内部的失败示例,但我不确定不会有。
【解决方案2】:

作为 Rex Kerr 出色回答的补充:

应该注意的是,多线程上下文中不可变集合的最常见用例不受此问题的影响。这可能会影响您的唯一情况是当您做了一些您一开始可能不应该做的事情

例如你有一个变量var x: Vector[Int],你从一个线程A写入并从另一个线程B读取。

如果您将x 标记为@volatile,则不会有问题,因为the volatile write introduces a memory barrier。因此,您将永远能够观察到处于不一致状态的 Vector。在读写时使用synchronized { }块或使用java.util.concurrent.atomic.AtomicReference时也是如此。

如果您x 标记为@volatile,您可能会观察到向量处于不一致状态(不仅仅是错误的元素,而是内部不一致! )。但在这种情况下,您的代码可能一开始就被破坏了。当您将在 B 中看到 A 的变化时,它完全未定义

你可能会看到他们

  • 立即
  • 在您的程序中的其他地方出现内存屏障之后
  • 根本没有

取决于您运行的架构、月相等。正如 Viktor Klang 所说:"Unsafe publication is unsafe..."

请注意,如果您使用更高级别的并发框架(例如 akka Actor),还可以保证消息的接收者无法看到处于不一致状态的不可变集合。

【讨论】:

  • 请注意,如果你用@volatile 标记它,JMM 实际上并不能保证你的向量是安全的,只是你会正确地看到外部类(而不是它在内部指向的任何东西)。我真的希望 JMM 更强大一点,这样你就可以指望内存屏障实际上存在。
猜你喜欢
  • 2015-04-15
  • 2012-08-22
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多