面试官竟然不知道Java单例模式双重检验锁一定要加volatile!

线程安全的单例模式本质上其实也是单次初始化,所以可以用Balking模式:

class Singleton{
  private static
    Singleton singleton;
  //构造方法私有化  
  private Singleton(){}
  //获取实例(单例)
  public synchronized static 
  Singleton getInstance(){
    if(singleton == null){
      singleton=new Singleton();
    }
    return singleton;
  }
}

这个实现但是性能却很差,因为互斥锁synchronized将getInstance()方法串行化了,那有没有办法可以优化一下它的性能呢?

办法当然是有的,那就是DCL,一旦Singleton对象被成功创建之后,就不会执行synchronized(Singleton.class){},即此时getInstance()方法的执行路径是无锁的,从而解决了性能问题。
使用volatile禁止编译优化。获取锁后的二次检查,出于安全性。

class Singleton{
  private static volatile 
    Singleton singleton;
  //构造方法私有化  
  private Singleton() {}
  //获取实例(单例)
  public static Singleton 
  getInstance() {
    //第一次检查
    if(singleton==null){
      synchronize{Singleton.class){
        //获取锁后二次检查
        if(singleton==null){
          singleton=new Singleton();
        }
      }
    }
    return singleton;
  }
}

也可以使用DCL优化性能,双重检查中的第一次检查,完全是出于对性能的考量:避免执行加锁操作,因为加锁操作很耗时。而加锁之后的二次检查,则是出于对安全性负责。双重检查方案在优化加锁性能方面经常用到,ReadWriteLock实现缓存按需加载功能时,也用DCL。



对于 T t = new T();
其实有如下字节码指令完成

_new 'org/openjdk/jol/T'
dup
INVOKESPECIAL org/openjdk/jol/T.<init> ()V
astore 1
return

线程一 new 到一半时,m=0,发生重排序
这时线程 2 来了!看到 t 已经指向了一个半初始化的实例了!

这个概率很小,但是并发如淘宝,都是可能发生的!所以必须要加!

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值