一、DCL(Double-Checked Locking) 懒汉式单例核心实现代码
public class Singleton {
// 1. 必须使用 volatile 关键字(核心关键点)
private volatile static Singleton instance;
// 私有构造函数:防止外部直接实例化
private Singleton() {}
// 双重检查锁获取实例
public static Singleton getInstance() {
// 2. 第一次检查:性能优化(避免已创建实例时的锁竞争)
if (instance == null) {
// 3. 加锁:保证实例创建的原子性和互斥性
synchronized (Singleton.class) {
// 4. 第二次检查:线程安全保障(防止并发场景下重复创建)
if (instance == null) {
// 5. 创建对象(需 volatile 禁止指令重排)
instance = new Singleton();
}
}
}
return instance;
}
}
二、双重检查(Double Check)的设计逻辑
2.1 第一次检查(First Check)
| 核心目的 |
逻辑解释 |
| 性能优化 |
当实例已创建(高并发下绝大多数场景),直接返回实例,避免进入同步代码块的锁竞争开销 |
| 触发条件 |
仅当 instance == null 时,才进入加锁逻辑 |
2.2 第二次检查(Second Check)
| 核心目的 |
逻辑解释 |
| 线程安全 |
防止多线程并发通过第一次检查后,重复创建实例 |
| 场景示例 |
1. 线程A、B同时通过第一次检查(此时instance为null)<br> |
- 线程A先抢到锁,创建实例后释放锁<br>
- 线程B拿到锁后,通过第二次检查发现instance已非null,直接返回,避免重复创建 |
三、Synchronized 关键字的作用
3.1 核心功能
- 原子性:保证
instance = new Singleton() 操作的不可分割性
- 互斥性:同一时刻仅允许一个线程执行同步代码块内的逻辑
3.2 必要性说明
若缺失 synchronized,多线程会同时进入实例创建逻辑,导致多个实例被创建,完全破坏单例模式的唯一性约束。
四、Volatile 关键字的核心作用
- 被它修饰的变量的可见性:线程对变量进行修改后,要立刻写回主内存;
- 线程对变量读取的时候,要从主内存读,而不是缓存;
- 在它修饰变量上的操作禁止指令重排序。
4.1 为什么需要 Volatile?