并发(五):我也谈谈双重检查与对象的安全发布

本文探讨了在Java中实现单例模式时遇到的双重检查锁定问题,并分析了其失败的原因,即对象构造过程中的非原子性和指令重排序。此外,还提供了四种确保对象安全发布的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在单例的实现模式中,很多大师都说过双重检查对JAVA不适用,比如说《设计模式》的作者闫宏,原因都是说JAVA语言构造器问题,具体说来大概都是以下两种:
1. JAVA对象的构造过程不是原子操作;
2. JAVA对象的构造化为本地代码时,有可能发生指令重排序现象;

我们先看双重检查的单例代码实现:

public class DoubleCheck {

    private static DoubleCheck instance;

    private DoubleCheck() {
    }

    public static DoubleCheck getInstance() {
        if(instance == null) {
            synchronized (DoubleCheck.class) {
                //  双重检查
                if(instance == null) {
                    //  构造器的赋值操作不是原子,有可能出现先赋值后初始化的现象
                    instance = new DoubleCheck();
                }
            }
        }       
        return instance;
    }
}

首先说明一点,构造器问题导致的单例失败现象是很难复现的,即使上述的单例双重检查失败了,请记住,也只会出现未构造完成的对象,绝不会出现构造器被调用两次的情况。

双重检查失败的原因在于,构造器的赋值操作会分解为很多步骤,有可能刚赋值(分配内存与默认值),还没来得及执行初始化方法,CPU就已经切换到另一个线程,而这个线程刚好又在执行第一次检查,从而返回了一个未初始化完成的对象。

所以,双重检查的问题也就变成了对象如何安全发布的问题?而安全发布一个对象有四种办法:
1. 直接对静态属性进行初始化(懒汉模式);
2. 用volatile或AtomicReference发布对象(双重检查成立);
3. 用诸如synchronizedMap、synchronizedList、ConcurrentMap的安全容器发布对象;
4. 在同步块中发布对象(静态方法加锁模式);

综上所述,只要能安全地发布对象,那么单例模式就是安全的,所以单例模式的实现也就至少有四种实现方法。

 结论

双重检查归根到底还是在于对象的安全发布问题,通过使用第2、3条对象的安全发布规则,双重检查也能应用于并发编程实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值