使用Volatile变量来安全发布Immutable对象

dev*_*ull 4 java multithreading

我发现了这句话:

在正确构造的对象中,无论对象如何发布,所有线程都将看到最终字段的正确值.

那么为什么使用volatile变量来安全地发布Immutable对象呢?

我真的很困惑.任何人都可以通过一个合适的例子说清楚吗?

And*_*yle 5

在这种情况下,波动性只会确保新对象的可见性; 根据JSR-133的初始化安全保证,碰巧通过非易失性字段获取对象的任何其他线程确实会看到最终字段的正确值.

尽管如此,使变量不变不会受到伤害; 从内存管理的角度看是正确的; 和有必要在构造函数初始化(虽然不应该有任何这些不可变的对象)非最终字段.如果您希望在线程之间共享变量,则需要确保足够的同步以提供可见性 ; 虽然在这种情况下你是对的,但构造函数的原子性没有危险.

感谢Tom Hawtin指出我完全忽视了JMM对最终领域的保证; 之前的错误答案如下.


volatile变量的原因是在对象的构造和变量的赋值之间建立了一个先发生关系(根据Java Memory Model).这实现了两件事:

  1. 从不同线程后续读取该变量可以保证看到新值.如果不将变量标记为volatile,则这些线程可以看到引用的陈旧值.
  2. 之前发生的关系放在什么重新排序的编译器可以做到的极限.如果没有volatile变量,变量的赋值可能在对象的构造函数运行之前发生 - 因此其他线程可以在完全构造之前获得对该对象的引用.

由于不可变对象的基本规则之一是您在构造函数期间不发布引用,因此这可能是第二点.在没有适当的并发处理多线程环境中,它可能的对象的引用,是"发表"该对象已建成之前.因此,另一个线程可以获取该对象,看到它的一个字段是null,然后看到这个"不可变"对象已经改变.

请注意,如果您有其他适当的同步原语,则不必使用volatile字段来实现此目的 - 例如,如果分配(以及所有后续读取)在synchronized给定监视器上的块中完成- 但是在"独立"中感觉,标记变量volatile是告诉JVM"这可能被多个线程读取的最简单方法,请在该上下文中安全地进行分配".