Appearance
源码:labs/java/runtime-concurrency/singleton-dcl/src/SingletonDcl.java
- 原始路径:
labs/java/runtime-concurrency/singleton-dcl/src/SingletonDcl.java - 类型:
java
java
/**
* 双重检查锁(DCL)+ volatile 的懒加载单例。
*
* 特点:
* - 懒加载:第一次 getInstance() 才进入同步块创建
* - 线程安全:✅ 内层 synchronized + volatile 安全发布
* - 性能:🚀 仅首次创建时加锁,之后外层 if 直接返回,适合高频 get
* - 复杂度:中高,易漏 volatile 或写错双重检查,面试高频
*
* Java 场景:
* - ✅ 需要懒加载且构造要传参 / 依赖运行时配置的单例
* - ✅ 高并发下频繁 getInstance() 的无参单例(也可用静态内部类替代)
* - 库代码若不能依赖类加载时机,有时显式 DCL 更清晰
*
* Android 场景:
* - ✅ Application 里按配置延迟 init 的管理器(baseUrl、开关等)
* - ✅ 不能在类加载阶段就 new 的对象(避免过早触达 SDK / Context)
* - 注意:单例若持 Context,必须用 applicationContext,与是否 DCL 无关
* - Kotlin 项目更常见 object / Hilt;DCL 多见于 Java 遗留或需显式 init 参数时
*
* 为什么一定要 volatile:
* - 防止 new SingletonDcl() 指令重排:引用先可见、构造未完成 → 半初始化对象
*
* 关键点:
* - 外层 if:已创建则跳过锁
* - 内层 if:锁内再判空,保证只 new 一次
* - volatile:禁止上述重排,保证安全发布
*
* 局限:
* - 手写易错;无参普通单例优先考虑静态内部类或 enum
*
* 如何运行:
* - javac -d out src/*.java
* - java -cp out SingletonDcl
*
* 预期输出:
* - 两次输出同一个对象引用,same instance = true
*
* 易错点:
* - 去掉 volatile 后单线程仍可能“看起来正常”,并发语义已破坏
*/
public class SingletonDcl {
private static volatile SingletonDcl instance;
private SingletonDcl() {
}
public static SingletonDcl getInstance() {
if (instance == null) {
synchronized (SingletonDcl.class) {
if (instance == null) {
instance = new SingletonDcl();
}
}
}
return instance;
}
public static void main(String[] args) {
SingletonDcl a = SingletonDcl.getInstance();
SingletonDcl b = SingletonDcl.getInstance();
System.out.println("实例 A 内存地址: " + a);
System.out.println("实例 B 内存地址: " + b);
System.out.println("是否为同一单例对象 (a == b): " + (a == b));
/*
* 静态阅读参考输出:
* 实例 A 内存地址: SingletonDcl@7ad041f3
* 实例 B 内存地址: SingletonDcl@7ad041f3
* 是否为同一单例对象 (a == b): true
*/
}
}