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;
}
}
对于这段代码其实可问的有很多,比如:
- 为什么构造函数要使用 private?
- 为什么要进行两次
if (instance == null)
? synchronized (Singleton.class)
在这里的作用是什么?synchronized (Singleton.class)
中为什么要使用Singleton.class
?使用其他的可以么?例如- 变量 instance 为什么要使用
volatile
修饰?
这里我们只关注第 5 个 问题,为什么 变量 instance 要使用 volatile
。
首先我们看如果不使用 volatile
会有什么影响。我们先看这个过程:
- 第一个
if (instance == null)
,如果为 false,则不需要执行下面的代码了,提高了程序的性能。 - 如果
instance == null
,即使是多线程,也会因为 synchronized 的存在,只会有一个线程执行下面的代码。
- 当第一个获得锁的线程创建完成后 singleton对象后,其他的线程也会在第二次判断 singleton一定不会为 null,则直接返回已经创建好的singleton对象。
细看上面的逻辑是没任何问题的,但是大明哥告诉你不加 volatile 就是有问题,那问题出在哪里呢?我们先来复习一下创建对象过程,实例化一个对象要分为三个步骤:
- 给 singleton 对象分配内存空间
- 调用 Singleton 类的构造函数等,初始化 singleton 对象
- 将 singleton 对象指向分配的内存空间,这步一旦执行了,那 singleton 对象就不等于null了
我们知道编译器或 CPU 为了提供程序的执行效率,会对代码和指令进行重排序,上面步骤2、3 可能会发生重排序,那么过程就变成这样了:
- 给 singleton 对象分配内存空间
- 将 singleton 对象指向分配的内存空间
- 调用 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] ,回复【面试题】 即可免费领取。