2024-03-23  阅读(61)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1955698867

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 的内存分配操作指令重排到构造函数之外


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文