主要作用
- 保证共享变量可见性
-
防止指令重排
volatile如何禁止指令重排序的?
volatile关键字通过“内存屏障”来防止指令被重排序。
下面是基于保守策略的JMM内存屏障插入策略:
- 在每个volatile写操作的前面插入一个StoreStore屏障
- 在每个volatile写操作的后面插入一个StoreLoad屏障
- 在每个volatile读操作的后面插入一个LoadLoad屏障
- 在每个volatile读操作的后面插入一个LoadStore屏障
DCL单例模式
JDK 1.5中才完全修复volatile屏蔽指令重排序的语义,在此之前无法安全地使用DCL(双锁检测)来实现单例模式。
public class Singleton {
private volatile static Singleton instance;
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
分析
instance = new Singleton();并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:
memory = allocate(); //1:分配对象的内存空间
initInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
当发生指令重排时,操作可能如下:
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址(此时对象还未初始化)
ctorInstance(memory); //2:初始化对象
注意
volatile关键字不能保证原子性