dcl、静态内部类、枚举均线程安全但适用场景不同:dcl需volatile+双重判空+synchronized,适合需运行时参数注入;静态内部类利用类加载机制,懒加载且天然安全,适合多数懒加载场景;枚举最简防破,适合无状态工具类。

并发环境下实现单例,关键不是“哪个最炫”,而是“哪个在当前约束下最稳”。DCL、静态内部类、枚举三者都能保证线程安全,但机制不同、适用边界明确,选错反而埋雷。
双重检查锁(DCL):可控但易出错
DCL 的核心价值是支持懒加载 + 避免每次调用都加锁。但它必须同时满足三个条件才真正安全:
- instance 字段必须用 volatile 修饰——否则 JVM 可能重排序对象初始化步骤,导致其他线程看到未构造完成的对象(字段仍为默认值),引发 NullPointerException 或逻辑异常;
- 两次判空缺一不可:第一次在 synchronized 外快速返回已存在实例,第二次在锁内防止重复创建;
- synchronized 块必须作用于类对象(如 Singleton.class),且锁范围仅限实例创建逻辑,不能扩大到整个方法。
它适合需要运行时参数注入(如依赖 Spring ApplicationContext)、或需精确控制初始化时机的场景。但对 Android 4.x 等老平台,volatile 的内存语义仍需格外验证;高竞争下 synchronized 块也可能成为瓶颈(尽管多数业务中可忽略)。
静态内部类:懒加载+天然线程安全
利用 JVM 类加载机制的“首次主动使用才触发初始化”特性,把单例实例放在一个静态内部类里:
- 外部类加载时,内部类不加载,不创建实例 → 实现懒加载;
- JVM 保证类初始化过程是原子且线程安全的 → 无需 synchronized 或 volatile;
- 无反射/反序列化绕过风险(相比 DCL),但无法阻止通过反射调用私有构造器(需额外防御)。
它比 DCL 更简洁、更少出错可能,适合绝大多数需要懒加载又不想操心 volatile 和锁细节的业务场景。缺点是无法传参构造,所有依赖必须在类加载时可获取(如静态配置、Spring Bean 已就绪)。
枚举单例:最简、最防破、但灵活性最低
Java 枚举天生是线程安全的单例,且 JVM 层面禁止反射创建新实例、反序列化也自动返回原实例:
- 构造发生在类加载时,但属于“懒汉式延迟”——首次引用该枚举常量才触发初始化;
- 完全规避指令重排、可见性、锁竞争等问题,代码仅一行(public enum Singleton { INSTANCE; });
- 不支持运行时参数传入,也不适配 Spring 等框架的生命周期管理(比如需要 postConstruct 回调时)。
它最适合工具类、配置类等无状态、无依赖、不需框架集成的场景。注意:Android 混淆后若未保留枚举类名,Enum.valueOf() 可能失败,需配置 keep 规则。
没有绝对优劣,只有是否匹配上下文。要防反射就选枚举;要懒加载又怕写错 volatile 就选静态内部类;要运行时动态构造且能兜住细节,DCL 仍是合理选择。










