此时会出现两个线程运行了SingletonDemo的构造方法
此时就违反了单例模式的规定,其构造方法在一些情况下会被执行多次
解决方式:
单例模式DCL代码
DCL (Double Check Lock双端检锁机制)在加锁前和加锁后都进行一次判断
public static SingletonDemo getInstance() { if (instance == null) { synchronized (SingletonDemo.class) { if (instance == null) { instance = new SingletonDemo(); } } } return instance; }不仅两次判空让程序执行更有效率,同时对代码块加锁,保证了线程的安全性
但是!还存在问题!什么问题?
大部分运行结果构造方法只会被执行一次,但指令重排机制会让程序很小的几率出现构造方法被执行多次
DCL(双端检锁)机制不一定线程安全,原因时有指令重排的存在,加入volatile可以禁止指令重排
原因是在某一个线程执行到第一次检测,读取到instance不为null时,instance的引用对象可能没有完成初始化。instance=new SingleDemo();可以被分为一下三步(伪代码):
memory = allocate();//1.分配对象内存空间 instance(memory); //2.初始化对象 instance = memory; //3.设置instance执行刚分配的内存地址,此时instance!=null步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,因此这种重排优化时允许的
所以如果3步骤提前于步骤2,但是instance还没有初始化完成指令重排只会保证串行语义的执行的一致性(单线程),但并不关心多线程间的语义一致性。
所以当一条线程访问instance不为null时,由于instance示例未必已初始化完成,也就造成了线程安全问题。
此时加上volatile后就不会出现线程安全问题
private static volatile SingletonDemo instance = null;因为volatile禁止了指令重排序的问题