//反编译后存在问题的的 double-checked locking
使用 volatile 修饰instance即可解决
public final class Singleton{
private Singleton(){};
private static /*volatile*/ Singleton instance=null;
public static Singleton getInstance(){
if (instance == null){ //实例没创建,才会进入同步代码块
synchronized(Singleton.class){
if(instance == null){ //防止首次创建instance 多个线程的并发
instance=new Singleton();
}
}
}
return instance;
}
}
反编译后指令:
0: getstatic #2
3: ifnonnull 37
6: ldc #3
8: dup
9: astore_0
10: monitorenter
11: getstatic #2
14: ifnonnull 27
17: new #3 //创建一个默认Singleton对象,其分配内存
20: dup //复制一个引用
21: invokespecial //利用一个对象引用,调用构造函数Singleton()
24: putstatic #2 //利用一个对象引用,赋值给 static instance
27: aload_0
28: monitorexit
29: goto 37
32: astore_1
33: aload_0
34: monitorexit
35: aload_1
36: athrow
37: getstatic #2
40: areturn
问题产生
主要看 17-24
因为编译器会进行指令重排,第24步可能会先于第21步执行,此时如果另一个t2线程刚执行到 第一个if (instance == null) 发现instance 已经有值了,将会跳转到37行,那么t2拿到的将是一个未初始化完毕的单例
解决 原理
加入写屏障后,21就不会排在24后面,保证了t2拿到的instance不是null,就是初始化完成后的单例