【问题标题】:Mutable Object safe publication with one only one thread writing一个唯一一个线程写入的可变对象安全发布
【发布时间】:2017-11-09 03:56:29
【问题描述】:

让非线程安全的可变对象X 在线程A 中构造。 A 传递 X,构建后,线程 BB 变异 XA 不再访问 X

X 的状态是否始终对B 可见?

X 是否有效地限制了线程?

我对 Java Concurrency in Practice 的阅读似乎表明 X 没有正确发布,但我不会在运行数百万次复制的测试台中对线程 B 造成任何问题。我怀疑这只是运气不好。

作为背景,X 代表了我无法控制的大量复杂类,这些类是由仅具有 Java 基本知识的建模者编写的。强烈建议X 没有同步块或其他并发机制或要求。

我目前正在通过让线程AX 传递B 调用的线程安全工厂来解决此问题,从而使X 线程受限。

【问题讨论】:

  • 您能否详细说明A 如何将X 传递给B
  • X 的集合被传递给实用程序类,该实用程序类将每个 X 的 Callable C 提交给完成服务。每个 X 都被传递到可调用的构造函数中。然后可调用对象在 X 上执行许多突变。
  • A CompletionService 包装了一个 ExecutorService ,因此似乎可以保证发生之前。谢谢。
  • 我怀疑这只是运气不好。不一定。仅仅因为规范不保证某些行为,并不意味着您将无法始终如一地重现它。但理论上它可能会因对代码、编译器、运行时、操作系统、处理器等的任何细微更改而中断。

标签: java concurrency


【解决方案1】:

仅对最终字段安全发布

Java 内存模型不保证对象 X 将完全发布(完全构造)到线程 A

为确保这一点,您需要使其不可变(所有成员字段最终)或同步。 引用JSR-133:

final 字段的语义已得到加强,以允许线程安全的不变性 没有显式同步。这可能需要采取措施,例如在 设置了 final 字段的构造函数的结尾。

唯一需要避免的是在构造函数完成之前将字段泄漏到类中。

测试

jcstress 实际上有一个示例项目来展示发布期间赛车的后果:JMMSample_06_Finals.java

请注意,必须采取一些措施来复制问题,例如使用多个字段。 JMM 的实现自然取决于您正在使用的特定 JRE,而且所使用的内存屏障的效果也取决于所使用的硬件。

在我使用 Oracle JDK 8 的硬件上,我无法使用 jcstress 的示例重现不安全的发布。

正在同步

所有同步操作之间存在“先发生”关系。这称为同步顺序。基本上,当您使用任何同步机制时,您可以保证在它之前的操作将在它之后可见。

Java Language Specification中总结的:

如果一个程序被正确同步,那么程序的所有执行将看起来是顺序一致的

实践中

实际上,由于在构造函数中执行的操作对使用该对象的线程不可见,因此很难遇到问题。 一个主要原因是同步机制的使用。您可以在 javadoc 中检查一些可确保发生前发生关系的操作:Memory Visibility

正如我在jcstress 示例中提到的,如今的 JRE 在确保结果一致方面似乎非常出色,即使它不需要根据语言规范。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-26
    相关资源
    最近更新 更多