DCL 了解吗?为什么 DCL 里面的变量需要用 volatile?

 作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题


代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等


联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等

DCL,Double-Checked Locking, 即双重检查锁定。很多小伙伴在单例模式中用到它,代码如下:

public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {                       // 1
            synchronized (Singleton.class) {          // 2
                if (instance == null) {               // 3
                    instance = new Singleton();       // 4
                }
            }
        }
        return instance;
    }
}

对于这段代码其实可问的有很多,比如:

  1. 为什么构造函数要使用 private?
  2. 为什么要进行两次 if (instance == null)?
  3. synchronized (Singleton.class) 在这里的作用是什么?
  4. synchronized (Singleton.class) 中为什么要使用 Singleton.class?使用其他的可以么?例如
  5. 变量 instance 为什么要使用 volatile 修饰?

这里我们只关注第 5 个 问题,为什么 变量 instance 要使用 volatile

首先我们看如果不使用 volatile 会有什么影响。我们先看这个过程:

  1. 第一个 if (instance == null),如果为 false,则不需要执行下面的代码了,提高了程序的性能。
  2. 如果 instance == null,即使是多线程,也会因为 synchronized 的存在,只会有一个线程执行下面的代码。
  • 当第一个获得锁的线程创建完成后 singleton对象后,其他的线程也会在第二次判断 singleton一定不会为 null,则直接返回已经创建好的singleton对象。

细看上面的逻辑是没任何问题的,但是码哥告诉你不加 volatile 就是有问题,那问题出在哪里呢?我们先来复习一下创建对象过程,实例化一个对象要分为三个步骤:

  1. 给 singleton 对象分配内存空间
  2. 调用 Singleton 类的构造函数等,初始化 singleton 对象
  3. 将 singleton 对象指向分配的内存空间,这步一旦执行了,那 singleton 对象就不等于null了

我们知道编译器或 CPU 为了提供程序的执行效率,会对代码和指令进行重排序,上面步骤2、3 可能会发生重排序,那么过程就变成这样了:

  1. 给 singleton 对象分配内存空间
  2. 将 singleton 对象指向分配的内存空间
  3. 调用 Singleton 类的构造函数等,初始化 singleton 对象

如果步骤 2、3发生了重排序就会导致第二个判断(if(singleton != null))会出错,因为它其实仅仅只是一个地址而已,对象还没有完成初始化,所以 return 的 singleton 对象是一个没有被初始化的对象,调用会报错,如下:

所以,DCL 使用 volatile 关键字,是为了禁止指令重排序,避免返回还没完成初始化的 singleton 对象,导致调用报错,也保证了线程的安全。确切地说是,就是使用 volatile防止了Java 对象在实例化过程中的指令重排,确保在对象的构造函数执行完毕之前,不会将 instance 的内存分配操作指令重排到构造函数之外

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值