可变对象安全发布,只有一个线程写入



让非线程安全、可变的对象X在线程A中构造。AX,后期施工,传递到螺纹BB变异XA再也不会访问X

X的状态是否总是对B正确可见?

X有效地限制线程吗?

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

作为背景,X表示许多复杂的类,我无法控制这些类是由只有 Java 基础知识的建模者编写的。 强烈建议X没有同步块或其他并发机制或要求。

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

发布仅对最终字段安全

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

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

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

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

测试

jcstress实际上有一个示例项目,用于在发布期间演示赛车的后果:JMMSample_06_Finals.java

请注意,必须做一些工作来复制问题,例如使用许多字段。 JMM的实现自然取决于您正在使用的特定JRE,并且所使用的内存屏障的影响也取决于所使用的硬件。

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

同步

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

正如Java语言规范中所总结的:

如果程序已正确同步,则程序的所有执行将显示为顺序一致

在实践中

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

另外,正如我在jcstress示例中提到的,现在的JRE似乎在确保一致的结果方面非常好,即使不需要根据语言规范也是如此。

相关内容

最新更新