为什么在单例类中不能使用双重检查锁来初始化对象

在网上看到过好多篇文章在说明双重检查锁在多个线程初始化一个单例类时到底为什么不行时在关键位置的描述模棱两可,今天我们就来看一下为什么不能用双重检查锁,问题到底出在了那里?


下面我们直接进入主题,为什么使用双重检查锁,原因是因为在多线程初始化一个单例类时我们要确保得到一个对象,又想再确保一个对象时得到更高的效率,所以就有了双重检查锁,使用双重检查锁初始化对象的代码如下

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:问题的根源出在这里
            }                                                       //8
        }                                                           //9
        return instance;                                            //10
    }                                                               //11
}    
为什么这样是不行的,问题的根源出在第7行(instance = new DoubleCheckedLocking();),创建一个对象可以分解为如下三步:

memory = allocate();   //1:分配对象的内存空间
ctorInstance(memory);  //2:初始化对象
instance = memory;     //3:设置instance指向刚分配的内存地址
上面三行伪代码中的2和3之间,可能会被重排序,2和3之间重排序之后的执行时序如下:


memory = allocate();   //1:分配对象的内存空间
instance = memory;     //3:设置instance指向刚分配的内存地址
                       //注意,此时对象还没有被初始化!
ctorInstance(memory);  //2:初始化对象
重排序不能影响单线程的执行语义,虽然这里2和3进行了重排序,但是只要保证2排在4前面执行,单线程内的执行结果不会被改变

时间线程A线程B
t1A1:分配对象的内存空间 
t2A3:设置instance指向内存空间 
t3 B1:判断instance是否为空
t4 B2:由于instance不为null,线程B将访问instance引用的对象(而这个时候对象还没有初始化)
t5A2:初始化对象 
t6A4:访问instance引用的对象 
线程B拿到一个未初始化的对象去操作,结果肯定就出错了


总结,到此为止我们只说明了为什么不可以用双重检查锁来初始化对象

THE  END!!!


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值