Java普通变量(未用volatile的修饰)的读和写是否是原子性的?

再读《java并发编程艺术》这本书的时候学到了volatile,作者在书中强调了,volatile可以保证读和写具有原子性。让我心生疑惑难道普通的变量读和写就不具有原子性了吗?我记得好多人都曾说过java普通变量的赋值和读取操作是原子性的。
然后这一堆概念在我脑子里变成了浆糊,它们似乎冲突着矛盾着,但又各自有各自的道理。
我对原子性真的感到十分疑惑。

我试着这样分析一下:
读取一个变量需要两部操作从主内存read出来,然后再load到工作内存,如果这两个指令中间重排序了其他的指令,那这样看来,读取一个变量真的不是原子操作。
给一个变量赋值,也是有多个指令组成,如果中间加入了其他的操作那也谈不上原子性了。
这样分析的话普通变量赋值和读取严格意义上谈不上原子操作,但是这样分析感觉别人说的赋值和读取是不可分割的又十分的冲突。
到底什么才是原子操作,就好比一个线程修改了一个普通变量,但是它没有及时写会主内存,它还是原子操作吗?

阅读 4.7k
5 个回答

可以参考这里:The Java® Language Specification Java SE 8 Edition

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Writes and reads of volatile long and double values are always atomic.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.

Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes to long and double values atomically or in two parts.

Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.

在虚拟机规范中并没有规定对非 volatile 的 double 和 long 的读写是原子的,但是虚拟机的具体实现也可以是原子的,对于 volatile 修饰的,读写必须为原子的。

经测试,在 32 位的 intel 机器上,不是原子的,在 64 位的 intel 机器上是原子的

volatile 关键字解决的不是原子性问题, 而可见性问题.
多核多 CPU 环境下, 每个线程可能因 CPU 高速缓存各自分离的原因, 一个线程看不到另一个线程修改的结果, 因为缓存还没有刷新到主内存的原因. volatile 关键字确保每次在做修改时都直接写入主内存(或同步高速缓存). 从而保障各个线程都能看到修改结果.

引用是, int等基本類型是

只有long和double不保證是

再读《java并发编程艺术》这本书的时候学到了volatile,作者在书中强调了,volatile可以保证读和写具有原子性

这本书我很早以前看过 我记得没错的话 书上也应该说了是32位JVM读写long double这种64位数据的情况会有问题吧...

到底什么才是原子操作,就好比一个线程修改了一个普通变量,但是它没有及时写会主内存,它还是原子操作吗?

不是,原子操作完成后的影响一定是可见的

新手上路,请多包涵

感觉评论都没有答都点上。
首先java对于写入和读取肯定是保证原子性的。但是楼主的理解有点误差,举个例子。
对于 Object obj = new Object();
有3步:
1:创建一块空的内存;
2:初始化这块空的内存;
3:将引用地址赋值给obj
第三步的赋值就是一个原子性操作。
再举个例子:int n=m;有两步,读m,写n;这是两个原子性操作,所以赋值就是一个原子性操作,没有问题;
然后 volatile 和原子性没有关系,它只解决了可见性和重排序问题,可见性这个不迷糊,问题出在重排序的概念没有理解好;
拿刚刚的例子来说,volatile 只保证这三个步骤不会乱而已,并不能保证原子性的哈~

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题