Java Volatile保证可变对象的安全发布?

Java Volatile保证可变对象的安全发布?,java,multithreading,concurrency,volatile,safe-publication,Java,Multithreading,Concurrency,Volatile,Safe Publication,通过阅读实践中的Java并发 我可以看到: 要安全地发布对象,对对象的引用和对象的状态必须同时对其他线程可见。正确构造的对象可以通过以下方式安全发布: 从静态初始值设定项初始化对象引用 将对它的引用存储到volatile字段或原子引用中 将对它的引用存储到正确构造的对象的最终字段中 将对它的引用存储到由 锁 然而,我对第二个成语感到困惑。由于volatile只能保证引用对另一个线程可见,但是它没有它所引用的对象构造的同步。那么,它如何保证可变对象被正确构造,构造这个对象的线程被另一个线程中断

通过阅读实践中的Java并发

我可以看到:

要安全地发布对象,对对象的引用和对象的状态必须同时对其他线程可见。正确构造的对象可以通过以下方式安全发布:

  • 从静态初始值设定项初始化对象引用
  • 将对它的引用存储到volatile字段或原子引用中
  • 将对它的引用存储到正确构造的对象的最终字段中
  • 将对它的引用存储到由 锁
然而,我对第二个成语感到困惑。由于
volatile
只能保证引用对另一个线程可见,但是它没有它所引用的对象构造的同步。那么,它如何保证可变对象被正确构造,构造这个对象的线程被另一个线程中断了呢


结果:
volatile
保证始终从所有线程共享的内存中读取变量-如果值在
volatile
变量中发布,则必须在之前完全构造该变量。
换句话说,如果
volatile
值未发布,那么其他线程就不会知道了——最有可能的是“正在进行的构造”的结果可能存在于CPU缓存或JVM用作“我为自己的目的使用的空间;你这个微不足道的Java代码,不要问里面有什么,这不关你的事”.

我们需要证明,构造一个对象并将其分配给一个易失性变量发生在从该变量读取之前

如果x和y是同一线程的动作,并且x在程序顺序中位于y之前,那么hb(x,y)

因此,从线程的角度来看,对象的构造在分配给可变变量之前发生

如果动作x与后续动作y同步,那么我们还有hb(x,y)

以及:

如果hb(x,y)和hb(y,z),那么hb(x,z)

如果我们可以证明写可变变量(动作y)与读变量(动作z)是同步的,那么我们可以使用before的传递性来说明构造对象(动作x)发生在读对象之前。幸运的是:

对易失性变量v的写入(§8.3.1.4)与任何线程对v的所有后续读取同步(其中“后续”是根据同步顺序定义的)

因此,我们可以看到,以这种方式发布时,任何线程都可以看到正确构造的对象

volatile只能保证引用对另一个线程是可见的,但它不同步它所引用的对象构造

对。你是对的。您可以在下面的问题中找到有关volatile变量的更多内部细节:

那么,它如何保证可变对象被正确构造,构造这个对象的线程被另一个线程中断了呢

为了线程安全,您必须使用其他编程结构:使用
synchronized
结构或
synchronized
结构的替代方案

请参阅以下相关SE问题:


谢谢你的回答,它清楚地回答了我的问题。顺便说一句,据我所知,“final”关键字也可以保证这一点,但为什么会这样呢?我假设它背后的机制不同?@GuifanLi该机制不同,并在JLS 17.5中解释。但我认为关键部分是这样的:“当一个对象的构造函数完成时,它被认为是完全初始化的。一个线程只有在该对象完全初始化后才能看到对该对象的引用,它才能保证看到该对象的最终字段的正确初始化值。”,确保带有
final
字段的对象的构造函数没有泄漏对
this
的引用,并且在对象完全初始化之前,任何东西都无法看到该对象。这提供了可视性,很好的解释。但我还有一个问题。程序顺序规则定义为偏序。在这种情况下,JVM仍然可以自由更改x和y的显示顺序。我们怎样才能保证它不会发生。@RavindraRanwala这是一个偏序,因为它只被限制向与它同步的其他线程呈现程序顺序的外观。在这种情况下,读取volatile变量与写入变量同步。这样的读卡器可以检测到的任何执行变化都是不允许的。@erickson,这意味着读操作是在写操作完成时执行的。因此,尽管写入的顺序是X->Y或Y->X,但读者仍能看到一致的最终结果。太好了,现在我明白了。谢谢!如果变量是对数组的引用呢?例如,volatile int[]arr=new int[5]。我知道arr将安全发布,发布后如何,我将arr[0]=100。这个线安全吗?我的观点是,这能保证arr[0]=100的安全发布吗?@GuifanLi据我所知/理解,发布后创建的实例(或数组)的内容没有任何保证。不过,我可能是错的(现在有点忙着寻找答案)。