单例模式知识点

本文介绍单例模式,即一个类只能构建一个对象的设计模式。分析了懒汉模式线程不安全问题,引出双重校验锁机制,但该机制未考虑JVM指令重排。以实例说明指令重排可能导致返回未初始化完成对象,最后提出用volatile修饰对象解决此问题。

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

单例模式:简单来说就是一个类只能构建一个对象的设计模式。

单例模式懒汉模式的代码实现:

public class Singleton{
    //私有构造方法
    private Singleton(){}

    //单例对象
    private static Singleton instance = null;

    //静态工厂方法
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

饿汉模式存在线程不安全的问题,如果Singleton类刚被初始化,instance对象此时为空,这时两个线程同时访问getInstance方法,就可能同时创建两个对象。对代码进行修改如下:

public class Singleton{
    //私有构造方法
    private Singleton(){}

    //单例对象
    private static Singleton instance = null;

    //静态工厂方法
    public static Singleton getInstance(){
        //双重检测机制
        if(instance == null){
            //同步锁
            synchronized(Singleton.class){
                //双重检测机制
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

双重校验锁机制的单例模式虽然增加了一定的安全性,但是它没有考虑到JVM编译器的指令重排机制,可能还会造成一定的问题。

JVM的指令重排:

拿一个简单的例子说,比如语句instance = new Singleton(),会被编译器编译成如下JVM指令。

memory?= allocate(); ????//1: 分配对象的内存空间?

ctorInstance(memory);??//2:初始化对象?

instance?= memory;?????//3:设置instance指向刚分配的内存地址

但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序。

memory?= allocate(); ????//1: 分配对象的内存空间? 

instance?= memory;?????//3:设置instance指向刚分配的内存地址

ctorInstance(memory);??//2:初始化对象?

在高并发情况下,假如线程A执行instance = new Singleton();完成指令1和3后,准备进行指令2,此时instance对象未完成初始化,但instance已经不再指向null。此时线程B抢占到CPU资源,执行 if(instance == null)时,返回的结果是false,从而返回了一个没有初始化完成的instance对象。

解决上述问题需要利用关键字volatile来修饰instance对象,如下优化:

public class Singleton{
    //私有构造方法
    private Singleton(){}

    //单例对象
    private volatile static Singleton instance;

    //静态工厂方法
    public static Singleton getInstance(){
        //双重检测机制
        if(instance == null){
            //同步锁
            synchronized(Singleton.class){
                //双重检测机制
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile 修饰符在此处的作用就是阻止变量访问前后的指令重排,从而保证了指令的执行顺序。

即指令的执行顺序是严格按照上文的 1、2、3 步骤来执行,从而保证对象不会出现中间态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值