【JUC并发编程】单例模式中的双检锁模式为什么必须加 volatile?

文章讨论了Java并发编程中的单例模式实现,特别是双检锁模式(DCL)。在DCL中,volatile关键字对于确保线程安全至关重要,因为它防止指令重排,避免未初始化的对象被多线程访问。当singleton对象被volatile修饰时,其状态变化对所有线程可见,确保了对象初始化的正确顺序,从而解决了并发问题。
摘要由CSDN通过智能技术生成

【JUC并发编程】单例模式中的双检锁模式为什么必须加 volatile?

什么是单例模式

单例模式指的是,保证一个类只有一个实例,并且提供一个可以全局访问的入口。

双重检查锁模式的写法

/**
 * @description: 单例模式
 * @author: blblccc
 * @date: 2023/3/7 10:52
 */
public class SingletonTest {

    private static volatile SingletonTest instance;

    private static synchronized SingletonTest getInstance() {
        if (instance == null){
            synchronized (SingletonTest.class){
                if (instance == null){
                    instance = new SingletonTest();
                }
            }
        }
        return instance;
    }
}

在双重检查锁模式中为什么需要使用 volatile 关键字

我们在双重检查锁模式中,给 singleton 这个对象加了 volatile 关键字,那**为什么要用 volatile 呢?**主要就在于 singleton = new Singleton() ,它并非是一个原子操作,事实上,在 JVM 中上述语句至少做了以下这 3 件事:

image-20230313111933844

  1. 第一步是给 singleton 分配内存空间;
  2. 然后第二步开始调用 Singleton 的构造函数等,来初始化 singleton;
  3. 最后第三步,将 singleton 对象指向分配的内存空间(执行完这步 singleton 就不是 null 了)。

禁止指令重排

这里需要留意一下 1-2-3 的顺序,因为存在指令重排序的优化,也就是说第2 步和第 3 步的顺序是不能保证的,最终的执行顺序,可能是 1-2-3,也有可能是 1-3-2。

如果是 1-3-2,那么在第 3 步执行完以后,singleton 就不是 null 了,可是这时第 2 步并没有执行,singleton 对象未完成初始化,它的属性的值可能不是我们所预期的值。假设此时线程 2 进入 getInstance 方法,由于 singleton 已经不是 null 了,所以会通过第一重检查并直接返回,但其实这时的 singleton 并没有完成初始化,所以使用这个实例的时候会报错,详细流程如下图所示:

image-20230313112146918

线程 1 首先执行新建实例的第一步,也就是分配单例对象的内存空间,由于线程 1 被重排序,所以执行了新建实例的第三步,也就是把 singleton 指向之前分配出来的内存地址,在这第三步执行之后,singleton 对象便不再是 null。

这时线程 2 进入 getInstance 方法,判断 singleton 对象不是 null,紧接着线程 2 就返回 singleton 对象并使用,由于没有初始化,所以报错了。最后,线程 1 “姗姗来迟”,才开始执行新建实例的第二步——初始化对象,可是这时的初始化已经晚了,因为前面已经报错了。

使用了 volatile 之后,相当于是表明了该字段的更新可能是在其他线程中发生的,因此应确保在读取另一个线程写入的值时,可以顺利执行接下来所需的操作。在 JDK 5 以及后续版本所使用的 JMM 中,在使用了 volatile 后,会一定程度禁止相关语句的重排序,从而避免了上述由于重排序所导致的读取到不完整对象的问题的发生。

volatile 作用

正确的双重检查锁定模式需要需要使用 volatile。

volatile主要包含两个功能。

  • 保证可见性。使用 volatile定义的变量,将会保证对所有线程的可见性。
  • 禁止指令重排序优化。

由于 volatile禁止对象创建时指令之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小颜-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值