定义长变量或双变量时volatile关键字如何影响原子性



我最近在阅读java中的思考,被下面的内容所迷惑:

但是,如果在定义长变量或双变量时使用volatile关键字,则可以获得原子性(对于简单的赋值和返回)(注意,在Java SE5之前,volatile不能正常工作)。

为什么我可以得到原子性,如果我使用volatile关键字时定义一个长或双变量?JVM被允许执行64位数量的读写(长和双变量)作为两个独立的32位操作,如果我定义一个长或双变量作为易失性两个独立的32位操作将不会被线程机制中断?

整段内容:

原子性适用于基本类型上的"简单操作",除了long和double类型。除long和double之外的其他基本变量的读写保证是作为不可分割(原子)操作进出内存的。但是,允许JVM将64位数量(长变量和双变量)的读和写作为两个单独的32位操作来执行,这增加了上下文切换可能发生在操作过程中间的可能性读取或写入,然后不同的任务可能会看到不正确的结果(这有时被称为word撕裂,因为您可能只在部分值被更改后才看到值)。但是,如果在定义长变量或双变量时使用volatile关键字,则可以获得原子性(对于简单的赋值和返回)(注意,在Java SE5之前,volatile不能正常工作)。不同的jvm可以自由地提供更强的保证,但是您不应该依赖于特定于平台的特性。

易失性字段立即写入主存,读取从主存进行。所以即使我把长或双精度变量定义为易失性变量,32位操作也可以发生并改变主存中的长或双精度值,在写长或双精度值时包含两个32位操作,而第一个32位操作可以发生在主存中改变值之前。谢谢。

在这种情况下,最好在JLS中查找:

17.7。双、长字符的非原子处理:

对于Java编程语言内存模型的目的,a对非易失性long或double值的单次写入被视为两次独立的写操作:每个32位的一半写一次。这可能导致a线程看到64位值的前32位的情况一次写入,另一次写入32位。

volatile long值和double值的读写操作始终是原子操作。

对引用的写和读总是原子性的,不管是32位还是64位值

顺便说一句,"Volatile字段会立即写入主存,而读取则发生在主存中"并不是真的。这是一种可能的实现,但是JVM实际如何实现volatile提供的保证是它自己的决定。根据底层硬件的缓存一致性协议,还有其他更好的解决方案。

另一方面,可以想象在一些低级硬件上保证这一点将涉及一些软件锁以避免读/写撕裂。效率低下吗?是的,但是如果JVM愿意,它完全可以自由地这样做。

最新更新