这是对象的安全发布吗?
我有一个课程项目
class Item { public int count; public Item(int count) { this.count = count; } }
然后,我将在其他类的字段中引用Item
class Holder { public Item item; public Holder() { item = new Item(50); } }
这个新的Item对象可以安全发布吗? 如果没有,为什么? 根据Java Concurrency in Practice,新的Item是在没有完全构造的情况下发布的,但在我看来,新的Item是完全构造的:它的引用不会被转义,并且它的引用和它的状态同时发布,所以消费者线程不会看到过时的价值。 或者是可见性问题。 我不确切知道原因。
这个新的Item对象可以安全发布吗? 如果没有,为什么?
该问题围绕指令的优化和重新排序。 当你有两个线程使用没有同步的构造对象时,可能会发生这样的情况:编译器决定为了效率而重新排序指令并为对象分配内存空间并在它与构造函数完成之前将其引用存储在item
字段中字段初始化。 或者它可以重新排序内存同步,以便其他线程以这种方式感知它。
如果将item
字段标记为final
则构造函数保证完成该字段的初始化作为构造函数的一部分。 否则,在使用锁之前,您必须同步锁定。 这是Java语言定义的一部分 。
这是另外几个参考:
- 双重检查锁定被打破
- 同步和Java内存模型
是的,存在可见性问题,因为Holder.item
不是final
。 因此,无法保证在Holder
构造完成后,另一个线程将看到其初始化值。
正如@Gray指出的那样,JVM可以在没有内存障碍(可以通过锁定(同步), final
或volatile
限定符)创建的情况下自由重新排序指令。 根据具体情况,您必须在此处使用其中一个以确保安全发布。