int 基元类型的易失性声明

2022-09-04 20:18:41

我引用了Oracle关于原子访问的Java文档

  • 读取和写入对于引用变量和大多数基元变量(除长变量和双精度变量之外的所有类型)都是原子的。
  • 读取和写入对于所有声明的变量(包括长变量和双精度变量)都是原子的。volatile

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

但是我看到代码使用基元类型的显式声明来实现原子访问;如果不这样做,不能保证原子访问。volatileint

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

我错过了什么吗?


答案 1

第一个语句不是指使引用变量和基元变量(和 除外)易失性longdouble

它说读取写入所有引用变量和所有基元,除了和都是原子的(默认情况下)。要使读取写入原子它们必须是 .longdoublelongdoublevolatile

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

关于同一文档的以下段落

原子操作不能交错,因此可以使用它们而不必担心线程干扰。但是,这并不能消除同步原子操作的所有需求,因为内存一致性错误仍然可能。使用易失性变量可降低内存一致性错误的风险,因为对易失性变量的任何写入都会与同一变量的后续读取建立“发生之前”关系。

因此,像 where is an (例如) 这样的语句是原子的,但是如果您希望赋值对任何后续读取线程可见,您仍然需要具有易失性a = 1aint

读取/写入长/双变量是一个复合操作,使其易失性可确保它是原子的。


答案 2

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

As 和基元占用的空间是 (64 位) 的两倍,更新其值可以发生在两个 32 位块中。因此,在没有易失性的情况下,您可以看到或介于这两个步骤之间的值。其他基元变量的情况并非如此。doublelongintlongdouble

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

当字段被声明为易失性时,编译器和运行时会注意到此变量是共享的,并且不应与其他内存操作重新排序对其上的操作进行重新排序。易失性变量不会缓存在寄存器或缓存中,因为它们对其他处理器隐藏,因此读取易失性变量始终返回任何线程的最新写入。

Java并发实践:3.1.4 易失性变量


推荐