单例模式是最常用的创建型设计模式之一,核心目标是保证一个类在整个应用生命周期内只有一个实例,并提供全局唯一的访问入口。本文梳理了单例模式的 5 种经典实现方式,分析各方案的优缺点及适用场景,同时给出工业级最佳实践。
一、懒汉式单例模式(加锁版,基础版不加锁,线程不安全)
核心特点:延迟初始化(第一次调用时创建实例),通过方法同步保证线程安全,但性能损耗大。
public class Singleton {
// 私有静态实例,初始为null(延迟加载)
private static Singleton instance;
// 私有构造方法:禁止外部new创建实例
private Singleton() {}
// 同步方法:保证多线程下唯一实例
public static synchronized Singleton getInstance() {
if (instance == null) { // 懒加载:仅在首次调用时创建
instance = new Singleton();
}
return instance;
}
}
优缺点
- ✅ 优点:完全延迟加载,无资源浪费;线程安全;实现简单。
- ❌ 缺点:synchronized 加在方法上,每次调用 getInstance() 都会加锁,即使实例已创建,高并发场景下性能差。
适用场景
并发量极低、对性能要求不高的简单场景(几乎不推荐在生产环境使用)。
二、饿汉式单例模式
核心特点:类加载时立即初始化实例,天然线程安全,但可能造成资源浪费。
public class Singleton {
// 类加载阶段(初始化阶段)就创建实例,JVM保证线程安全
private static final Singleton instance = new Singleton();
// 私有构造方法
private Singleton() {}
// 无锁获取实例,性能极高
public static Singleton getInstance() {
return instance;
}
}
优缺点
- ✅ 优点:线程安全(JVM 类加载机制保证);无锁开销,性能最优;实现简单。
- ❌ 缺点:非延迟加载,若实例创建依赖重资源(如数据库连接、大对象),且长期未使用,会造成资源浪费。
适用场景
实例创建成本低、启动后大概率会被使用的场景(如工具类、轻量级配置类)。
三、双重检查锁(DCL)单例模式
核心特点:兼顾延迟加载和线程安全,仅在实例未创建时加锁,性能接近饿汉式。需通过 volatile 解决指令重排问题。
public class Singleton {
// volatile关键字:1.保证可见性 2.禁止指令重排(关键!)
private static volatile Singleton instance;
// 私有构造方法
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查:实例已创建则直接返回,避免加锁
synchronized (Singleton.class) { // 类对象锁:仅当实例未创建时加锁
if (instance == null) { // 第二次检查:防止多线程并发创建
instance = new Singleton();
}
}
}
return instance;
}
}
关键解释
instance = new Singleton() 并非原子操作,JVM 会拆分为 3 步:
1、分配内存空间;
2、初始化实例;
3、将 instance 指向内存地址。
若不加 volatile,JVM 可能指令重排为 1→3→2,导致其他线程在第二步完成前,看到 instance 不为 null 但实例未初始化,引发空指针异常。
优缺点
- ✅ 优点:延迟加载;仅首次创建时加锁,高并发性能优异;线程安全。
- ❌ 缺点:实现稍复杂,易因遗漏 volatile 导致线程安全问题。
适用场景
高并发场景、实例创建成本高且需延迟加载的场景(生产环境常用)。
四、静态内部类单例模式(推荐)
核心特点:利用 JVM 类加载机制实现延迟加载 + 线程安全,兼顾性能与优雅性,是最推荐的非枚举实现方式。
public class Singleton {
// 私有构造方法
private Singleton() {}
// 静态内部类:独立于外部类,仅在被调用时加载
private static class InnerClass {
// JVM保证静态变量初始化的线程安全
private static final Singleton instance = new Singleton();
}
// 外部类调用时,才触发内部类加载和实例创建
public static Singleton getInstance() {
return InnerClass.instance;
}
}
关键解释
- 外部类 Singleton 加载时,静态内部类 InnerClass 不会被加载,实现延迟加载;
- 调用 getInstance() 时,InnerClass 被 JVM 加载,其静态变量 instance 由 JVM 保证线程安全地初始化;
- 静态内部类的加载是线程安全的,无需加锁。
优缺点
- ✅ 优点:延迟加载;线程安全;无锁开销,性能优;实现优雅,无指令重排风险。
- ❌ 缺点:无法通过反射完全防止实例被创建(可通过构造方法加校验规避)。
适用场景
绝大多数生产环境场景(平衡性能、优雅性、安全性的最优解)。
五、枚举单例模式(最佳实践)
核心特点:利用枚举的天然特性实现单例,防反射、防序列化破坏,是《Effective Java》推荐的最优方案。
// 枚举类天然单例,由JVM完全保证唯一性
public enum Singleton {
INSTANCE; // 唯一实例,JVM加载时初始化
// 单例的业务方法
public void doSomething() {
System.out.println("枚举单例执行业务逻辑");
}
// 可选:添加自定义属性/初始化逻辑
private String config;
// 枚举的构造方法默认私有,无需显式声明
Singleton() {
// 模拟初始化逻辑(如加载配置)
this.config = "default-config";
}
public String getConfig() {
return config;
}
}
// 使用方式(全局唯一)
public class Client {
public static void main(String[] args) {
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance1 == instance2); // true
instance1.doSomething(); // 调用业务方法
}
}
关键解释
- 枚举的实例由 JVM 在类加载时创建,天然线程安全;
- 反射无法创建枚举实例(Constructor.newInstance() 会抛出 IllegalArgumentException);
- 序列化时,枚举的 readResolve() 方法被 JVM 重写,保证反序列化后仍是原实例。
优缺点
- ✅ 优点:绝对线程安全;防反射 / 序列化破坏;实现极简;无需额外处理。
- ❌ 缺点:非延迟加载(枚举类加载时即初始化);部分开发者对枚举单例的认知不足。
适用场景
对单例唯一性要求极高的场景(如分布式系统、安全框架、核心配置类),是工业级最佳实践。
六、扩展:Spring 中的单例实现
Spring 容器中的 @Scope("singleton")(默认作用域)并非直接使用上述代码,而是通过容器管理实现单例:
1、Spring Bean 默认在容器初始化时创建(类似饿汉式);
2、通过 @Lazy 注解可实现延迟加载(类似懒汉式);
3、Spring 容器保证单例的线程安全,底层结合了双重检查锁 + 容器缓存机制。
七、总结:选型指南

核心原则:
1、简单场景用饿汉式,需延迟加载用静态内部类;
2、对安全性要求极致时,优先选择枚举单例;
3、避免手动实现复杂的单例逻辑,优先利用框架(如 Spring)的单例管理能力。
最终目的:
保证核心对象全局唯一,减少资源消耗;
SpringBoot 核心应用场景:
Spring 容器中默认所有 Bean 为单例(singleton)、ApplicationContext实例全局唯一、核心工具类(如Environment)单例