1. 什么是volatile关键字?
是JAVA语言提供的一种稍弱的同步机制,用来确保将变量的更新操作通知到其他线程。
- 保证可见性(MESI缓存一致性协议)
- 不保证原子性
- 禁止指令重排序(内存屏障)
保证可见性:
![](https://img.haomeiwen.com/i24459569/5ee90fc10130eb79.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虚拟机必须实现以下四种屏障
- LoadLoad屏障(两条读取的指令禁止重排)
- StoreStore屏障
- LoadStore屏障
- StoreLoad屏障
2. 解释一下对象的创建过程(new一个对象要分为几步)?
- 申请内存,赋默认值
- 调用构造方法,赋初始值
- 建立关联
3. DCL单例到底需不需要volatile?
![](https://img.haomeiwen.com/i24459569/6b23e27b8ae29695.png)
由于我们new一个对象分为3步,当new对象进行到一半的时候发生了指令重排序,就会造成半初始化的对象和引用建立了连接,如果这个时候有一个线程进来,就会直接拿走这个半初始化的对象。而volatile可以禁止指令重排序,所以要加volatile。
网友评论