双重检查锁定(dcl)单例需用volatile修饰实例并执行两次null检查:第一次在同步外避免无谓加锁,第二次在同步内防止多线程重复创建;volatile禁止重排序并保证可见性,缺一则dcl失效。
在 java 中,双重检查锁定(double-checked locking, dcl)是一种常见的单例实现方式,它通过 if 语句配合 synchronized 块实现线程安全且延迟初始化。关键在于两次 if 判断:第一次避免无谓加锁,第二次确保实例唯一性。
第一次 if:避免不必要的同步开销
在获取实例时,先检查实例是否已创建。若已存在,直接返回,跳过同步块,提升性能。
- 这是 DCL 的“第一重检查”,发生在同步块外
- 必须用
volatile修饰单例引用,防止指令重排序导致其他线程看到未初始化完成的对象 - 示例逻辑:
if (instance == null) { /* 进入同步块 */ }
同步块内第二次 if:保证实例创建的原子性
进入同步块后,再次检查实例是否为 null。因为可能多个线程同时通过了第一次检查,只有第一个进入的线程应创建实例。
- 这是“第二重检查”,防止多线程重复初始化
- 构造逻辑必须放在该
if内,例如instance = new Singleton(); - 若省略此检查,可能出现多个实例,破坏单例语义
volatile 关键字不可省略
volatile 不仅禁止指令重排序,还保证可见性:一个线程对 instance 的写入能立即被其他线程看到。
- 没有
volatile,JVM 可能将对象分配内存、调用构造器、赋值给引用三步重排序,导致其他线程拿到未完全初始化的对象 - 即使两次
if都存在,缺少volatile仍会导致 DCL 失效
完整可运行示例
以下是一个标准 DCL 单例实现,含必要注释说明 if 控制点:
public class Singleton {
private static volatile Singleton instance;
<pre class="brush:java;toolbar:false;">private Singleton() {} // 私有构造,防止外部实例化
public static Singleton getInstance() {
if (instance == null) { // 第一次检查:无锁,快速返回
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查:加锁后确认,防重复创建
instance = new Singleton(); // 构造实例(volatile 保障安全发布)
}
}
}
return instance;
}}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











