对于volatile的理解

2020-09-04  本文已影响0人  倚仗听江

1. 什么是volatile关键字?

是JAVA语言提供的一种稍弱的同步机制,用来确保将变量的更新操作通知到其他线程。

保证可见性:

缓存行.png

因为程序的局部性原理CPU会把数据按块读取到自己的缓存行中(三级缓存),当一颗CPU把缓存行中的数据改了,那么就会产生数据不一致的问题(内存中同一行的数据位于不同的缓存行中如何保证数据的一致性)。MESI 缓存一致性协议就是用来解决这个问题。
保证了多颗CPU之间缓存行的一致性。假设一个CPU更改了缓存行中的内容,就会把自己的缓存行改为Modified状态,写回内存,并通知其他CPU把这一缓存行改为Invalid。当被改为Invalid时,就需要去内存中重写读取。

不保证原子性:

对于i=1这个赋值操作,由于其本身是原子操作,因此在多线程程序中不会出现不一致问题,但是对于i++这种复合操作,即使使用volatile关键字修饰也不能保证操作的原子性,可能会引发数据不一致问题。

i++操作可以被拆分为三步:
1)线程读取i的值
2)i进行自增计算
3)刷新回i的值
不是一个原子操作,因此不保证原子性

禁止指令重排:

重排序是CPU底层的一种优化
在单线程的环境下,CPU在内部执行两条指令的时候,第一条指令比较慢,第二条指令比较快,二者之间也没有直接的关系。CPU有可能会让第二条指令先执行,但一定要保证执行的结果一致,也就是as-if-serial。
as-if-serial语义的意思是:不管怎么重排序,单线程程序的执行结果不能被改变。
保证单线程环境下不能改变程序执行的结果**,存在数据依赖关系的指令不允许重排序
内存屏障就是用来解决指令重排的

JVM要求所有的java虚拟机必须实现以下四种屏障

2. 解释一下对象的创建过程(new一个对象要分为几步)?

  1. 申请内存,赋默认值
  2. 调用构造方法,赋初始值
  3. 建立关联

3. DCL单例到底需不需要volatile?

DCL.png

由于我们new一个对象分为3步,当new对象进行到一半的时候发生了指令重排序,就会造成半初始化的对象和引用建立了连接,如果这个时候有一个线程进来,就会直接拿走这个半初始化的对象。而volatile可以禁止指令重排序,所以要加volatile。

上一篇 下一篇

猜你喜欢

热点阅读