在Java中安全发布不可变对象

aha*_*aha 8 java parallel-processing concurrency java.util.concurrent

我想了解是否volatile需要发布不可变对象.

例如,假设我们有一个不可变对象A:

// class A is immutable
class A {
  final int field1;
  final int field2;

  public A(int f1, int f2) {
    field1 = f1;
    field2 = f2;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我们有一个B从不同线程访问的类.它包含对类对象的引用A:

// class B publishes object of class A through a public filed
class B {
  private /* volatile? */ A toShare;

  // this getter might be called from different threads
  public A getA(){
    return toShare;
  }

  // this might be called from different threads
  public void setA(num1, num2) {
    toShare = new A(num1, num2);
  }
}
Run Code Online (Sandbox Code Playgroud)

从我的阅读似乎不可变对象可以通过安全地发布任何手段,这是否意味着我们并不需要声明toSharevolatile确保其存储的知名度?

Dim*_*rov 2

不,我们不能保证您会看到所有更新toShare共享数据字段的所有更新。这是因为您的共享数据不使用任何同步构造来保证其可见性或通过它跨线程可访问的引用的可见性。这使得它可以在编译器和硬件级别上进行大量优化。

\n\n

您可以安全地将字段更改toShare为引用 a String(对于您的所有目的而言,它也是不可变的),并且您可能(并且正确地)对其更新可见性感到更加不安。

\n\n

在这里,您可以看到我创建的一个基本示例,它可以显示更新如何丢失,而无需任何其他措施来发布对不可变对象的引用的更改。我在 JDK 8u65 和 Intel\xc2\xae Core\xe2\x84\xa2 i5-2557M 上使用 JVM 标志运行它-server,忽略可能抛出的错误NullPointerException,并看到以下结果:

\n\n
    \n
  • 如果没有safebe volatile,第二个线程不会终止,因为它看不到第一个线程所做的许多更改
  • \n
\n\n

控制台输出:

\n\n
[T1] Shared data visible here is 2147483647\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 什么时候safe改为volatile时,第二个线程与第一个线程一起终止
  • \n
\n\n

控制台输出:

\n\n
[T1] Shared data visible here is 2147483647\n[T2] Last read value here is 2147483646\n[T2] Shared data visible here is 2147483647\n
Run Code Online (Sandbox Code Playgroud)\n\n

PS 还有一个问题要问你 - 如果sharedData(而不是safe)发生了会发生什么volatile会发生什么?根据 JMM 的说法,可能会发生什么?

\n