绝对安全的单例实现只有枚举单例和静态内部类两种:枚举单例由JVM保障唯一性,天然防反射和反序列化;静态内部类需严格遵循规范并加反射防护。

Java 中多线程环境下写单例,要“绝对安全”,关键不是堆砌同步或加锁,而是把控制权交给 JVM 本身——它比任何手动同步都更可靠。目前真正能同时满足线程安全、懒加载、防反射、防反序列化、无性能开销这五项要求的,只有两种写法:枚举单例和静态内部类(配合必要防护)。其他方式都有明确短板。
首选:枚举单例(最简最稳)
这是《Effective Java》强烈推荐的方式,JVM 层面保障唯一性,天然抵抗所有常见攻击:
- 构造过程由 JVM 控制,多线程下只会初始化一次,无需任何 synchronized 或 volatile
- 自动防止反射攻击:枚举类的构造器无法被反射调用(JVM 级限制)
- 自动防止反序列化破坏:Enum 类型的 readObject 方法被 JVM 特殊处理,不会新建实例
- 写法极简,不易出错
示例:
public enum Singleton {
INSTANCE;
public void doWork() {
// 业务逻辑
}
}
立即学习“Java免费学习笔记(深入)”;
使用:Singleton.INSTANCE.doWork()
次选:静态内部类(兼顾兼容性与安全性)
适用于不能用枚举的场景(如需继承、实现接口、或历史项目强依赖 class 类型),但必须严格遵循规范:
- 内部类必须是 private static,且 INSTANCE 字段声明为 private static final
- 构造函数加反射防护:首次初始化时检测是否已有实例,避免通过反射绕过
- 内部类中禁止复杂逻辑(如读配置、连数据库)——一旦抛异常,该类后续永远不可用(NoClassDefFoundError)
- 外部代码绝不能主动引用
SingletonHolder.class,否则提前触发初始化
示例:
public class Singleton {
private Singleton() {
if (SingletonHolder.INSTANCE != null) {
throw new RuntimeException("Singleton already initialized");
}
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
慎用:双重检查锁(DCL)
虽广泛使用,但“绝对安全”需满足多个硬性条件,漏一即破:
-
instance字段必须用 volatile 修饰(防止指令重排序导致其他线程看到未初始化完成的对象) - 构造函数内要加反射防护(否则 new 指令可被反射绕过)
- 不能用于需要反序列化的场景(需额外实现
readResolve()) - 在部分老旧 JVM 或特殊硬件上,volatile 语义仍有边界风险(虽极小)
不满足以上全部,就不是“绝对安全”。
不推荐:饿汉式与同步方法
饿汉式(静态常量/静态块)线程安全,但不支持懒加载;同步方法(synchronized getInstance)虽安全,但每次调用都加锁,性能差且非必要。两者都不满足“延迟初始化 + 高并发友好”的现代需求。


















