静态内部类单例通过JVM类加载机制实现线程安全的懒加载:SingletonHolder类首次被主动使用时才初始化INSTANCE,其<clinit>方法由JVM保证只执行一次且天然同步,无需额外锁,编译后生成独立class文件,无外部类强引用。

Java 中用静态内部类实现单例,本质是借力 JVM 类加载机制——不是靠代码加锁,而是靠类初始化过程的天然同步保障线程安全与延迟加载。
为什么能延迟加载
外部类(如 ConfigService)加载时,其静态内部类 SingletonHolder 不会初始化。只有第一次调用 getInstance()、执行 return SingletonHolder.INSTANCE 这行代码时,JVM 才触发对 SingletonHolder 的“主动使用”,进而加载并初始化它。
- INSTANCE 实际创建发生在 SingletonHolder 的
<clinit>方法中,也就是首次访问时才发生 - 启动阶段不创建实例,不占用内存,真正懒加载
- 编译后生成独立的
Outer$Holder.class文件,无对外部类的强引用,利于 GC
为什么天然线程安全
JVM 规范强制规定:同一个类加载器下,一个类的初始化只会执行一次,且由类加载器自动加锁,多线程并发触发时,仅一个线程进入 <clinit>,其余阻塞等待。
- INSTANCE 必须声明为 public static final,确保对象构造完成后再发布,杜绝指令重排序导致的“逸出”
- 整个初始化过程原子、不可中断、无需
synchronized或volatile - 字节码干净,运行时零同步开销,性能接近直接读取静态字段
标准写法三要素
缺一不可,否则可能破坏语义或引入风险:
立即学习“Java免费学习笔记(深入)”;
- 外部类构造方法私有:private ConfigService() {},防止 new 调用,也为后续防反射留余地
- 静态内部类必须是 static private class SingletonHolder,且只含一个 public static final 实例字段
- getInstance() 方法体极简:return SingletonHolder.INSTANCE;,不判空、不加锁、不 new
注意事项与增强点
该模式默认不防御反射和反序列化攻击:
- 若需防反射,可在私有构造中加入校验逻辑,例如检查 SingletonHolder.INSTANCE != null
- 若需防反序列化,添加 readResolve() 方法,返回已有实例
- 不适用于带参构造场景——SingletonHolder 中只能调用无参构造函数
- 若构造过程抛异常,类加载失败会导致后续所有 getInstance() 抛 NoClassDefFoundError,建议在构造内做好容错


















