简单解释DCL单例可能存在的问题

本文详细解析了DCL单例模式中的并发问题及其实现细节,特别是针对多线程环境中可能出现的指令重排序问题进行了说明,并通过具体场景演示了如何避免获取到未初始化的对象。

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

public class DCLSingleton {

    private static DCLSingleton instance;
    public int value; 

    private DCLSingleton(){
        value = 1024;
        
    }
    public static DCLSingleton getInstance(){
        if(instance == null){
            synchronized (DCLSingleton.class){
                if (instance == null){
                    instance = new DCLSingleton(); //这不是个原子操作,JVM至少分为三个操作去执行
                        /**
                     * 1.正常情况
                         * 1.1 address = allocate()
                     * 1.2 <init>,此处会调用DCLSingleton的构造函数,给value赋值1024
                     * 1.3 instance = address
                     */

                    /**
                     * 2.指令重排序情况
                         * 2.1 address = allocate()
                     * 2.2 instance = address  这时候instance已经不为空了,别的线程是可以看到的
                         * 2.3 <init>,此处会调用DCLSingleton的构造函数,给value赋值1024
                     */
                }
            }
        }
        return instance;
    }
}

DCL单例代码如上,

多线程并发调用时,正常情况下我们期望每个线程拿到的 value值都是1024,但在指令重排序后某些线程拿到value的值可能为0

我们用至少三个线程的环境下,来模拟第二种指令重排后可能会出现的问题的过程:

1.线程A先拿到DCLSingleton.class的锁开始执行,线程B发现锁被占用,只好等待一下了

2.线程A在new DCLSingleton()的过程中,执行完2.2后被cpu踢出

注意:instance已经被赋值,不为空,但DCLSingleton的构造函数还没被调用

3.线程C杀出调用getInstance(), 由于instance已经不为空,C不会同步等待,会直接拿到一个没有调用构造函数的DCLSingleton实例,这时value就是0

4.线程A又开始去执行,接着new的未完成部分,在执行2.3时调用DCLSingleton的构造函数,这时会给value赋值1024

5.此后的线程B与其他线程获得value的值都为1024




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值