单例模式双重检验锁的volatile和两次判空

本文探讨了Java中实现单例模式的双重检查锁定(DCL)方法,解释了为何需要两次判空检查以确保线程安全和性能优化。同时,详细阐述了volatile关键字在此处的作用,防止指令重排序导致的并发问题,以避免空指针异常的发生。

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

首先是代码,经典的双重锁写法。

public class Singleton {
    private volatile static Singleton Instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(Instance == null){
            synchronized(Singleton.class){
                if(Instance == null){
                    Instance = new Singleton()}
            }
        }
        return Instance;
    }
}

先说为什么需要两次判空的原因,第一次判断是为了验证是否创建对象,避免多线程访问时每个线程都加锁,提升效率第二次判断是为了避免重复创建单例,因为可能会存在多个线程通过了第一次判断在等待锁,来创建新的实例对象。

例如:有三个线程,A与B同时调用getSingleton时,判断第一个if都为空,这时A拿到锁,进行第二层if判断,条件成立new了一个对象;由于Synchronized原因,B在外层等待,A创建完成,释放锁,B拿到锁,进行第二层if判断,条件不成立,结束释放锁。C调用getSingleton时第一层判断不成立,直接拿到singleton对象返回,避免进入锁,减少性能开销。

在说说为什么有了Synchronized却还需要volatile去修饰Instance。
volatile修饰变量只是为了禁止指令重排序,因为在 Instance = new Singleton(); 创建对象时,底层会分为四个指令执行:(下面是正确的指令执行顺序)
①、如果类没有被加载过,则进行类的加载
②、在堆中开辟内存空间 adr,用于存放创建的对象
③、执行构造方法实例化对象
④、将堆中开辟的内存地址 adr 赋值给被volatile修饰的Instance引用变量

如果Instance引用变量不使用volatile修饰的话,则可能由于编译器和处理器对指令进行了重排序,导致第④步在第③步之前执行,此时Instance引用变量不为null了,但是Instance这个引用变量所指向的堆中内存地址中的对象是还没被实例化的,实例对象还是null的;那么在第一次判空时就不为null了,然后去使用时就会报NPE空指针异常了。

双重检验是一种实现单例模式的机制。该模式的主要目的是在多线程环境下确保只有一个实被创建。它结合了懒汉模式饿汉模式的优点。 在双重检验机制中,使用了两个操作。第一次是为了避免不必要的同步操作,如果实已经被创建,就不需要再进入同步代码块。第二次是在synchronized块内部进行的,这样可以确保在多线程环境下只有一个线程可以创建实。 具体实现中,使用了volatile关键字来保证多线程下的可见性。而synchronized关键字则保证了在多线程环境下只有一个线程可以进入同步代码块,避免了多个线程同时创建实的问题。 总结来说,双重检验模式是一种在多线程环境下安全且高效地创建的方式。它通过两次使用volatilesynchronized关键字来保证线程安全可见性。这种模式在需要延迟实化的场景下非常适用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [单例模式----双重检查](https://blog.csdn.net/weixin_50005657/article/details/115803998)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [单例模式之「双重校验」](https://blog.csdn.net/weixin_44471490/article/details/108929289)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值