7

我有一个班级项目

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 是完全构造的:它的this引用不会转义,并且对它的引用和它的状态是同时发布的,所以消费者线程不会看到陈旧的值。或者是可见性问题。我不完全知道原因。

4

2 回答 2

16

这个新的 Item 对象可以安全地发布吗?如果不是,为什么?

问题围绕指令的优化和重新排序。当您有两个线程在没有同步的情况下使用构造对象时,编译器可能会为了效率而决定重新排序指令并为对象分配内存空间并将其引用存储在item字段中,然后再完成构造函数和字段初始化。或者它可以重新排序内存同步,以便其他线程以这种方式感知它。

如果将该item字段标记为,final则保证构造函数作为构造函数的一部分完成该字段的初始化。否则,您必须在使用锁之前对其进行同步。这是Java 语言定义的一部分。

这是另外几个参考:

于 2012-04-24T15:33:21.530 回答
6

是的,存在可见性问题,但Holder.item不是final。所以不能保证在构造Holder完成后另一个线程会看到它的初始化值。

正如@Gray 指出的那样,JVM 可以在没有内存屏障的情况下自由地重新排序指令(可以通过锁(同步)、afinalvolatile限定符创建)。根据上下文,您必须在此处使用其中一种以确保安全发布。

于 2012-04-24T15:33:33.993 回答