双重检查锁定的延时初始化:

 

 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与之对应。

posted on 2017-11-23 21:11  飞奔的菜鸟  阅读(367)  评论(0编辑  收藏  举报