我引用了Atomic Access
上的Oracle Java文档
- 读取和写入对于引用变量和大多数原始变量(除long和double之外的所有类型)都是原子的。
- 对于声明为
volatile
的所有变量(包括长变量和双变量),读取和写入都是原子的。
我理解volatile
的工作原理。但是提到明确声明volatile
long
和double
变量以在第二个语句中获得原子访问的要求,是对参考变量和大多数原始变量进行volatile
声明(在long和double之外的所有类型)第一个语句中可选。
但我看到代码在volatile
基元类型中使用显式int
声明来实现原子访问;并且不这样做不保证原子访问。
int variable1; // no atomic access
volatile int variable2; // atomic access
我错过了什么吗?
答案 0 :(得分:7)
第一个语句不是指引用变量和原始变量(long
和double
除外) volatile
。
它说读取所有引用变量的和写,除long
和double
之外的所有原语都是原子 (默认情况下)。要使读取和写 long
和double
原子,他们需要volatile
。< / p>
原子性与可见性没有任何关系。
关于同一文档的以下段落
原子操作不能交错,因此可以使用它们而不用担心线程干扰。但是,这并不能消除所有同步原子操作的需要,因为仍然可能存在内存一致性错误。使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都会建立与之后读取同一变量的先发生关系。
因此,像a = 1
这样的语句a
是int
(例如) atomic ,但你仍然需要 volatile 如果您希望任何后续读取线程可以看到该分配。
读取/写入long / double变量是一个复合动作,并使其 volatile 确保它是原子的。
答案 1 :(得分:5)
volatile
关键字不仅可以保证原子访问,还可以提供可见性保证。
当double
和long
原语占用int
(64位)空间的两倍时,它们的值可以在两个32位块中发生。因此,如果没有易失性,您可以在这两个步骤之间看到long
或double
的值。其他原始变量不是这种情况。
但原子访问与可见性不同。 volatile
关键字还保证在写入之后发生的其他线程上的变量的所有读取都将看到新值。这就是为什么仍然需要在其他原始类型上使用volatile
的原因。
当一个字段声明为volatile时,编译器和运行时 我们注意到这个变量是共享的,对它的操作不应该 与其他内存操作重新排序。挥发性变量未缓存 注册或隐藏在其他处理器中的高速缓存中,所以读取一个 volatile变量总是返回任何线程的最近写入。
实践中的Java并发:3.1.4易失性变量