双重检查锁定与延迟初始化

在java程序中,有时候需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化。
但是正确实现线程安全的延迟初始化需要一些技巧,否则容易出现问题。


public class UnsafeLazyInitialization{


private static UnsafeLazyInitialization instance;

public static UnsafeLazyInitialization getInstance(){

    if(instance == null)                                   //1:A线程执行
          instance = new UnsafeLazyInitialization();  //2:B线程执行
  }

}


线程A执行到代码1时,B线程执行代码2,线程A可能看到instance引用的对象还没有完成初始化。


public class UnsafeLazyInitialization{


private static UnsafeLazyInitialization instance;

public synchronized static UnsafeLazyInitialization getInstance(){

if(instance == null)                         //1:A线程执行
       instance = new UnsafeLazyInitialization();  //2:B线程执行
}

}


getInstance()方法做了同步处理,synchronized将导致性能开销。如果getInstance()方法被多个线程频繁调用,导致执行性能下降。反之,延迟初始化方案能提供满意的性能。




双重检查锁定,实现延迟初始化,降低同步的开销


public class DoubleCheckedLocking{                                  //1


   private static DoubleCheckedLocking instance;        //2

   public static DoubleCheckedLocking getInstance(){           //3

   if(instance == null){ //4:第一次检查
       synchronized(DoubleCheckedLocking.class){//5:加锁
          if(instance == null)              //6:第二次检查
               instance = new DoubleCheckedLocking();       //7:问题的根源所在
     }
  }
return instance;
}

}




在线程指定到第4行时,代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化。


memory = allocate();      //1:分配对象的内存空间
ctorInstance(memory);     //2:初始化对象
instance = memory;        //3:设置instance指向刚分配的内存地址


上述伪代码的2和3可能被重排序,造成延迟初始化问题。




解决办法:1)不允许2和3重排序
          2)允许2和3重排序,但不允许其他线程"看到"这个重排序

  
1)不允许重排序


public class DoubleCheckedLocking{                  //1


private volatile static DoubleCheckedLocking instance;     //2

public static DoubleCheckedLocking getInstance(){           //3

if(instance == null){ //4:第一次检查
     synchronized(DoubleCheckedLocking.class){//5:加锁
       if(instance == null)              //6:第二次检查
           instance = new DoubleCheckedLocking();       //7:
    }
 }
 return instance;
 }

}


volatile需要jdk5或更高版本。多线程环境时,加了volatile将禁止对象初始化和分配内存地址的重排序




2)基于类初始化的解决方案


public class InstanceFactory{


private static class InstanceHolder{
   public static InstanceHolder instance = new InstanceHolder();
}

public static InstanceHolder getInstance(){
   return IntanceHolder.instance;        //InstanceHolder类被初始化
}
}


类的初始化,对于每一个类或接口,都有一个唯一的初始化锁LC与之对于。保证多线程情况下,类只会被一个线程进行初始化。


总结:如果确实需要对实例字段使用线程安全的延迟初始化,建议使用volatile的延迟初始化方案。如果是需要对静态字段使用线程安全的延迟初始化,建议使用类初始化方案。


  
  













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值