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

Java 可变对象安全发布,只有一个线程写入,java,concurrency,Java,Concurrency,让非线程安全的可变对象X在线程A中构造A在构造后将X传递到线程BB变异X和A不再访问X X的状态是否始终对B可见 X是否有效地限制了线程 我在实践中对Java并发性的阅读似乎表明X没有正确发布,但我不能在运行数百万次复制的测试装备中为线程B造成任何问题。我怀疑这只是愚蠢的运气 作为背景,X表示我无法控制的大量复杂类,这些类是由只有Java基本知识的建模者编写的。强烈建议X没有同步块或其他并发机制或要求 我目前正在通过让线程A通过B调用的X线程安全工厂来解决这个问题,从而限制X线程 仅对最终字段安

让非线程安全的可变对象
X
在线程
A
中构造<代码>A在构造后将
X
传递到线程
B
B
变异
X
A
不再访问
X

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

X
是否有效地限制了线程

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

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


我目前正在通过让线程
A
通过
B
调用的
X
线程安全工厂来解决这个问题,从而限制
X
线程

仅对最终字段安全发布

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

为了确保这一点,您需要使其不可变(所有成员字段都是最终的)或同步。 引述:

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

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

测试

jcstress
事实上有一个示例项目来演示出版期间赛车的后果:

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

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

同步

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

如报告所述:

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

在实践中

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


正如我在
jcstress
示例中提到的,现在的JRE似乎很好地确保了结果的一致性,即使根据语言规范它不需要这样做。

您能否详细说明
A
如何将
X
传递给
B
?将X的集合传递给实用程序类,该实用程序类为每个X向完成服务提交一个可调用的C。每个X都被传递到可调用的构造函数中。然后可调用函数在X上执行许多突变。您是指
ExecutorService
?那么您应该很好了:CompletionService包装了ExecutorService,因此似乎保证了之前发生的情况。谢谢。我怀疑这只是愚蠢的运气。不一定。仅仅因为规范没有保证某些行为,并不意味着您将无法始终如一地复制它。但从理论上讲,只要对代码、编译器、运行时、操作系统、处理器等进行微小的更改,它就可能崩溃。