Java 读取对象的引用并读取对象’;JMM下的s字段

Java 读取对象的引用并读取对象’;JMM下的s字段,java,java-memory-model,Java,Java Memory Model,这一职位是在阅读以下内容后提出的: 和测试: @JCStressTest @State public class SynchronizedPublish { RacyBoxy boxie = new RacyBoxy(); @Actor void actor() { boxie.set(new Box(42)); // set is synchronized } @Actor void observer(IntResult1 r) { Box t =

这一职位是在阅读以下内容后提出的:

和测试:

@JCStressTest
@State
public class SynchronizedPublish {
  RacyBoxy boxie = new RacyBoxy();

  @Actor
  void actor() {
    boxie.set(new Box(42)); // set is synchronized
  }

  @Actor
  void observer(IntResult1 r) {
    Box t = boxie.get(); // get is not synchronized
    if (t != null) {
      r.r1 = t.x;
    } else {
      r.r1 = -1;
    }
  }
}
作者说有可能
r.r1==0
。我同意你的看法 那个但是,我对一个解释感到困惑:

实际的失败来自这样一个事实:在内存模型下,读取对象的引用和读取对象的字段是不同的

我同意

在内存模型下,读取对象的引用和读取对象的字段是不同的 但是,我看不出它对结果有什么影响

请帮助我理解它


注意:如果有人对演员感到困惑。它的意思是:在一个线程中运行。

我认为它解决了一个常见的微观概念,即人们阅读代码的顺序一致性。对实例的引用在一个线程中可用这一事实并不意味着它的构造函数已经设置。换句话说:读取实例与读取实例字段是不同的操作。许多人认为,一旦能够观察到一个实例,就需要运行构造函数,但由于缺少读取同步,上述示例并非如此。

我将在这里稍微补充一下公认的答案-如果没有一些障碍,则绝对无法保证一旦看到引用(想一些线程可以获得一个引用)-该构造函数中的所有字段都已初始化。如果我没有弄错的话,我实际上早在一段时间前就已经回答了你的一个问题

在具有最终字段的构造函数之后插入了两个屏障
LoadLoad
LoadStore
;如果您考虑到它们的名称,您会注意到,构造函数之后的任何操作都不能在其内部重新排序:


还请注意,在当前的
x86
内存模型下,您不可能打破这一点,因为它是一个(太?)强内存模型;因此,这些障碍在
x86
上是免费的,它们根本不被插入,因为操作没有重新排序。

那么,您的意思是?第一个线程(参与者)已发布引用,但对象未初始化是。然后,观察者读取对box的引用(
boxie.get()
),由于引用不是
null
,因此观察者读取
t.x==0
。是的,发布实例并调用其构造函数并不总是原子的,除非确保对象可以安全发布。
@JCStressTest
@State
public class SynchronizedPublish {
  RacyBoxy boxie = new RacyBoxy();

  @Actor
  void actor() {
    boxie.set(new Box(42)); // set is synchronized
  }

  @Actor
  void observer(IntResult1 r) {
    Box t = boxie.get(); // get is not synchronized
    if (t != null) {
      r.r1 = t.x;
    } else {
      r.r1 = -1;
    }
  }
}
Load -> Load (no Load can be re-ordered with a previous Load)
Load -> Store (no Store can be re-ordered with a previous Load)