volatile修饰对象的时候,能否防止对象指令重排?

本文探讨了一个看似线程安全的单例模式中隐藏的半初始化对象问题,通过分析实例创建的三步指令执行顺序,揭示了竞态条件下的风险。随后,作者提出使用`volatile`关键字确保`SingletonUser`实例的可见性,以修复这一问题。

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

一、先看一个线程安全的DCL(double check lock)单例例子

public class SingletonUser {
    //有可能拿到一个空的对象
    private static SingletonUser instance = null;
    private SingletonUser() {}
    public SingletonUser getInstance(){
        if(instance ==null){
            synchronized (SingletonUser.class){
                if(instance ==null){
                    instance = new SingletonUser();
                }
            }
        }
        return instance;
    }
}

二、分析

这个看似安全的单例模式,在竞态条件下会出现一个半初始化对象的问题。那么我们要从创建单例对象这个语句说起了,这个语句实际在jvm是分成三步完成的,上伪代码,这一段代码在jvm进行优化的时候会乱序,有可能会变成1、3、2的执行顺序

instance = new SingletonUser ();
//这个new 并且赋值的语句在jvm中其实可以抽象成三条指令
memory = allocate();    //1:给对象开辟一块内存
initInstance(memory);   //2:初始化对象
instance = memory;      //3:instance指向分配好的内存

当线程A执行到对象引用执行分配好的内存时,这时对象还未初始化,线程B此时调用getInstance()方法,判断引用已经不为null,因此直接返回,此时对象是半初始化状态,使用会导致异常出现。

三、解决方案

public class SingletonUser {
    //增加volatile修饰对象
    private static volatile SingletonUser instance = null;
    private SingletonUser() {}
    public SingletonUser getInstance(){
        if(instance ==null){
            synchronized (SingletonUser.class){
                if(instance ==null){
                    instance = new SingletonUser();
                }
            }
        }
        return instance;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值