int 基本类型的 volatile 声明

volatile declaration on int primitive type

我引用的是关于 Atomic Access

的 Oracle Java 文档
  • Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  • Reads and writes are atomic for all variables declared volatile (including long and double variables).

我了解 volatile 的工作原理。但是提到在第二个语句中为 longdouble 变量明确声明 volatile 以获得原子访问的要求,是为引用变量和大多数原始变量进行 volatile 声明(除 long 和 double 之外的所有类型)在第一个语句中可选。

但我看到代码在 int 原始类型中使用显式 volatile 声明来实现原子访问;并且不这样做不能保证原子访问。

int variable1;          // no atomic access
volatile int variable2; // atomic access

我错过了什么吗?

第一条声明不涉及制作引用变量和原始变量(longdouble除外)volatile.

它说 读取 写入 所有参考变量和除 longdouble 之外的所有原语是atomic(默认)。要使 读取 写入 longdouble atomic,他们需要成为 volatile.

原子性与可见性没有任何关系。

同一文档的以下段落

Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible. Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable.

所以,像 a = 1 这样的语句,其中 a 是一个 int (例如)是 atomic 但你仍然需要 volatile 如果您希望分配对任何 后续 阅读线程可见。

Reading/Writing 到 long/double 变量是一个复合操作并使其成为 volatile 确保它是原子的。

volatile 关键字不仅仅保证原子访问,它还附带 可见性保证

因为 doublelong 原语需要 int(64 位)的 space 的两倍,更新它们的值可以发生在两个 32 位块中。因此,如果没有 volatile,您可以在这两个步骤之间看到 longdouble 的值。其他原始变量不是这种情况。

但是原子访问与可见性不同。 volatile 关键字还保证在写入变量后发生在其他线程上的所有变量读取都将看到新值。所以这就是为什么仍然需要在其他原始类型上使用 volatile 的原因。

When a field is declared volatile, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations. Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.

Java 并发实践:3.1.4 可变变量