volatile的原理
volatile 的底层实现原理是内存屏障,Memory Barrier(Memory Fence)
加入 volatile 关键字后,写指令(被 volatile 修饰的变量在对此变量修改时)会加入写屏障,读指令(被 volatile 修饰的变量在对此变量读取时)会加入读屏障。
- 写屏障:
- 保证有序性:不会将写屏障之前的代码排在写屏障之后
- 保证可见性:保证在写屏障之前,对共享变量的修改,都同步到主存中
- 读屏障
- 保证有序性:不会将读屏障之后的代码排在读屏障之前
- 保证可见性:保证在读屏障之后,对共享变量的读取,加载的是主存中最新的数据
double-checked locking 问题
由 double-checked locking 单例模式为例
public final class Singleton {
private Singleton (){}
private static Singleton INSTANCE = null;
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class) {
if(INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
关键的字节码部分 =》可能出现指令重排序(21 和 24 顺序互换)
...
17: new #3 // class com/zhl/n5/Singleton
20: dup
21: invokespecial #4 // Method "<init>":()V
24: putstatic #2 // Field INSTANCE:Lcom/zhl/n5/Singleton;
...
double-checked locking 解决
在 INSTANCE 对象加上 volatile
关键字,禁用重排序,保证有序性。
private static volatile Singleton INSTANCE = null;