今天,分享一下单例模式之DCL分析。
public class SingletonDemo {
private static volatile SingletonDemo singletonDemo = null;
private SingletonDemo(){
System.out.println("double check Lock双端检锁机制");
}
//DCL(double check Lock双端检锁机制)
public static SingletonDemo getSingleton(){
if (singletonDemo==null){
synchronized (SingletonDemo.class){
if(singletonDemo==null){
singletonDemo = new SingletonDemo();
}
}
}
return singletonDemo;
}
public static void main(String[] args) {
}
}
注意:DCL(double check lock)双端检锁机制不一定线程安全,因为有指令重排的存在,加入volatile可以解决,因为volatile可以禁止指令重排。
问:为什么DCL不一定线程安全?
分析:当某一个线程执行第一次检测,读取到的instancce不为null时,instance的引用对象可能还没有完全初始化。
instance = new SingleonDemo()可以分为以下3步完成(伪代码)
1、分配对象内存空间:memory = allocate();
2、初始化对象:instance(memory);
3、设置instance指向刚分配内存地址(此时instanc!=null):instance = memory;
以上步骤2和步骤3不存在数据依赖关系,而且无论重排还是重排后程序的执行结果哎单线程中并没有改变,因此这种重排优化是允许的 :1->3->2,即
1、分配对象内存空间:memory = allocate();
2、设置instance指向刚分配内存地址(此时instanc!=null,但是对象还没有初始化完成!):instance = memory;
3、初始化对象:instance(memory);
但是指令重排只会保证串行语义的执行的一致(单线程),并不关心多个线程的语义一致性,所以当一条线程访问instance不为null时,由于instance实例未必初始化完成,也就造就了线程安全问题。
努力奋斗,不负韶华!