双重检查锁定的延时初始化:
1 class DoubleCheckLocking{ 2 private DoubleCheckLocking() {} 3 private static DoubleCheckLocking instance; 4 public static DoubleCheckLocking getInstance(){ 5 if (instance == null){ 6 synchronized (DoubleCheckLocking.class){ 7 if (instance == null) 8 instance = new DoubleCheckLocking(); 9 } 10 } 11 return instance; 12 } 13 }
双重检查锁定看起来似乎很完美,但这是一个错误的优化!执行程序读取到第5行instance不为空时,instance引用的对象可能还没有完成初始化。
程序第8行所做的操作如下:
1:分配对象的内存空间
2:初始化对象
3:设置instance指向刚分配的内存地址
步骤2和3之间可能发生重排序,导致上面的问题。
解决方法:
1:不让2和3重排序
2:允许2和3重排序,但不允许其他线程“看到”这个重排序。
解决方案1:基于volatile的解决方案
1 class DoubleCheckLocking{ 2 private DoubleCheckLocking() {} 3 private volatile static DoubleCheckLocking instance; 4 public static DoubleCheckLocking getInstance(){ 5 if (instance == null){ 6 synchronized (DoubleCheckLocking.class){ 7 if (instance == null) 8 instance = new DoubleCheckLocking(); 9 } 10 } 11 return instance; 12 } 13 }
当声明对象的引用为volatile后,2和3之间的重排序,在多线程环境中将被禁止。
解决方案2:基于类初始化的解决方案
基础:JMM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JMM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。基于此来实现另一种线程安全的延迟初始化方案。
1 class InstanceFactory{ 2 private InstanceFactory(){} 3 private static class InstanceHolder{ 4 public static InstanceFactory instance = new InstanceFactory(); 5 } 6 public static InstanceFactory getInstance(){ 7 return InstanceHolder.instance; //这里将导致InstanceHolder类的初始化 8 } 9 }
假设两个线程并发执行 getInstance()方法,则InstanceHolder已经初始化完InstanceFactory对象。
Java语言规范规定,对于每一个类或者接口C,都有一个唯一的初始化锁LC与之对应。
来源:https://www.cnblogs.com/xcyz/p/7887008.html