双重检查锁定的延时初始化:
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