Java volatile为什么以及如何表示原子读/写?

Java volatile为什么以及如何表示原子读/写?,java,volatile,atomicity,Java,Volatile,Atomicity,首先,我知道volatile不会使多个操作(如I++)原子化。这个问题是关于单个读或写操作的 我最初的理解是volatile只强制执行内存屏障(即其他线程将能够看到更新的值) 现在我注意到,volatile还使单个读或写原子化。例如,给定两个线程,都将不同的值写入volatile long x,那么x最终将恰好代表其中一个值 我很好奇这怎么可能。在32位系统上,如果两个线程并行写入64位位置,并且没有“适当”的同步(即某种锁),则结果可能会混淆。为清楚起见,让我们使用一个示例,其中线程1将0L写

首先,我知道volatile不会使多个操作(如
I++
)原子化。这个问题是关于单个读或写操作的

我最初的理解是volatile只强制执行内存屏障(即其他线程将能够看到更新的值)

现在我注意到,volatile还使单个读或写原子化。例如,给定两个线程,都将不同的值写入
volatile long x
,那么x最终将恰好代表其中一个值

我很好奇这怎么可能。在32位系统上,如果两个线程并行写入64位位置,并且没有“适当”的同步(即某种锁),则结果可能会混淆。为清楚起见,让我们使用一个示例,其中线程1将0L写入,而线程2将-1L写入相同的64位内存位置

T1 writes lower 32 bit
T2 writes lower 32 bit
T2 writes upper 32 bit
T1 writes upper 32 bit
结果可能是0x0000FFFF,这是不需要的。
volatile
如何防止这种情况


我在其他地方也读到过,这通常不会降低性能。如何在速度影响很小的情况下同步写操作?

volatile确保线程读取的是该点的最新值,但它不会同步两次写操作


如果一个线程写入一个普通变量,它会将值保留在线程中,直到某些特定事件发生。如果线程写入一个volatile变量,它会立即更改该变量的内存。

您关于
volatile
仅强制执行内存屏障(即刷新处理器缓存)的陈述是错误的。它还意味着
volatile
值的读写组合的before-before关系。例如:

class Foo {
  volatile boolean x;
  boolean y;

  void qux() {
    x = true; // volatile write
    y = true;
  }

  void baz() {
    System.out.print(x); // volatile read
    System.out.print(" ");
    System.out.print(y);
  }
}
当您从两个线程运行这两个方法时,上述代码将要么打印
true-false
true-true
要么打印
false-false
,但决不会打印
false-true
。如果没有
volatile
关键字,就不能保证后面的条件,因为JIT编译器可能会对语句重新排序

与JIT编译器确保这种情况的方法相同,is可以保护程序集中的64位值读写<代码>volatile值由JIT编译器显式处理,以确保其原子性。某些处理器指令集通过特定的64位指令直接支持这一点,否则JIT编译器将模拟它

JVM比您所期望的更复杂,而且经常在没有完整范围的情况下对其进行解释。考虑涵盖所有细节。

在32位系统上,如果两个线程并行写入64位位置,并且没有“适当”的同步(即某种锁),则结果可能会混淆

如果一个变量没有标记为volatile,就会发生这种情况。现在,如果字段标记为volatile,系统会做什么?这里有一个资源可以解释这一点:

几乎所有处理器都至少支持一条粗粒度的屏障指令,通常称为围栏,它保证在围栏之前启动的所有加载和存储将在围栏之后启动的任何加载或存储之前严格排序[…]如果可用,您可以将易失性存储作为原子指令实现(例如x86上的XCHG)并省略屏障。如果原子指令比存储加载屏障便宜,这可能更有效


本质上,处理方提供了实施担保的设施,可用的设施取决于处理方。

据我所知,OP首先表示他们已经知道这一点,并询问如何处理挥发物(可能询问如何实施)你必须自己处理同步问题,而不是考虑“易失性”。只是出于好奇,以前发生的事情不是因为记忆障碍而建立起来的吗?是的,记忆障碍(当你使用同义词围栏时可能更清楚)是一条排序指令。因此,仅隐式地使用术语“内存屏障/围栏”就意味着一个在建立之前发生的事件,总是吗?好吧,内存屏障本身没有什么描述性。在指令之间建立了一个围栏。如果在上面的示例中有另一个
volatile
variable
z
,您会读到de>z而不是
x
,没有提供任何保证。即使这样,对z的读取也保证提供最新的值(因为它声明为volatile。)。感谢您的解释:)