如果使用易失性引用发布,另一个线程是否可以看到处于不一致状态的有效不可变对象?

Rom*_*vyi 4 java concurrency multithreading

根据 Java Concurrency in Action 如果我们有以下类:

public class Wrapper {
  private int num;

  public Wrapper(int num) {
    this.num = num;
  }

  public void assertCorrectness() {
    if (num != num)
      throw new AssertionError("This is false");
  }
}
Run Code Online (Sandbox Code Playgroud)

并且我们初始化此类的实例并以非安全方式发布它(例如通过简单的公共字段),那么如果从另一个线程调用,assertCorrectness() 可能确实会抛出 AssertionError。换句话说,这意味着另一个线程可能会看到对该实例的最新引用,但实例本身的状态可能已过时(因此线程可以看到对象存在,但它处于部分构造/不一致的状态)。

另一方面,据说通过易失性引用发布此类的实例被认为是安全的。然而,我的理解是,易失性只是保证任何线程总是会看到引用的最新版本,但看不到被引用的对象的状态。因此,我们可以确定,如果一个线程将 Wrapper 类的新实例分配给 易失性字段,那么所有其他线程都会看到引用已更新。但是是否存在他们仍然会看到处于不一致/部分构造状态的对象的风险?

Kay*_*man 7

不,因为volatile被使用建立了一种发生之前的关系。如果没有它,各种重新排序和其他事情是被允许的,这使得不一致的状态成为可能,但是有了它,JVM 必须给你预期的结果。

在这种情况下,不volatile用于可见性效果(线程看到最新值),而是由 happens-before 提供的安全发布。在解释其用途时,经常会忽略该功能。volatile