《单例模式》|设计模式系列第02篇
系列:创建型模式 · 单例模式(Singleton Pattern)
关键词:全局唯一实例、私有构造、懒汉/饿汉、双重检查锁、静态内部类、枚举单例
一、什么是单例模式
单例模式(Singleton Pattern):保证一个类在整个应用中只有一个实例对象,并且提供一个全局访问点来获取该实例。

属于创建型设计模式,核心两点:
- 类自身负责创建唯一实例
- 对外暴露统一方法获取实例,禁止外部new
核心意图
控制实例数量,节省资源;保证全局共用同一个对象,例如配置管理器、连接池、日志对象。
适用场景
- 资源重量级对象:数据库连接池、线程池
- 全局配置类、系统信息管理器
- 日志工具类、缓存管理器
- 需要全局唯一计数器
⚠️ 不适合:无状态轻量工具类,强行单例反而增加复杂度。
二、UML结构图
类:Singleton
- 私有静态成员变量:
instance(Singleton instance) - 私有构造方法:
Singleton(),防止外部 new - 公共静态获取实例方法:
getInstance()

三、常见实现方案(Java)
1. 饿汉式(Eager Singleton)
类加载时直接初始化实例,天生线程安全;缺点:类加载就创建,没有懒加载。
public class EagerSingleton {
// 类加载直接实例化
private static final EagerSingleton INSTANCE = new EagerSingleton();
// 私有构造,禁止外部 new
private EagerSingleton() {}
// 全局访问点
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
✅ 优点:简单、线程安全
❌ 缺点:类加载即创建实例,浪费内存(实例从未使用时)
2. 懒汉式(Lazy Singleton,非线程安全)
第一次调用getInstance()才创建实例(懒加载),多线程环境会创建多个实例,生产禁用。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
✅ 优点:懒加载,用到才创建
❌ 缺点:多线程不安全
3. 双重检查锁 DCL(Double-Check Locking,推荐常用)
懒加载 + 线程安全,volatile 防止指令重排。
public class DclSingleton {
// volatile 禁止指令重排
private static volatile DclSingleton instance;
private DclSingleton() {}
public static DclSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (DclSingleton.class) {
if (instance == null) { // 第二次检查
instance = new DclSingleton();
}
}
}
return instance;
}
}
✅ 优点:懒加载、线程安全、性能好
❌ 缺点:实现稍复杂;反射可以破坏单例
关键点:
instance = new DclSingleton()并非原子操作,volatile 必不可少。
4. 静态内部类(Static Inner Class,推荐)
利用类加载机制实现懒加载,线程安全,写法简洁。
public class StaticInnerSingleton {
private StaticInnerSingleton() {}
// 静态内部类,只有调用时才加载
private static class SingletonHolder {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}
public static StaticInnerSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
✅ 优点:懒加载、线程安全、代码简洁
❌ 缺点:依然可以被反射破坏
5. 枚举单例(Enum Singleton,Effective Java 推荐)
防止反射、序列化破坏单例,最安全。
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("枚举单例业务方法");
}
}
// 使用:EnumSingleton.INSTANCE.doSomething();
实例调用演示代码:
/**
* 单例模式演示:验证五种实现均为同一实例。
*/
public class SingletonDemo {
public static void main(String[] args) {
System.out.println("========== 1. 饿汉式 ==========");
EagerSingleton eager1 = EagerSingleton.getInstance();
EagerSingleton eager2 = EagerSingleton.getInstance();
eager1.sayHello();
System.out.println("同一实例: " + (eager1 == eager2));
System.out.println("\n========== 2. 懒汉式 ==========");
LazySingleton lazy1 = LazySingleton.getInstance();
LazySingleton lazy2 = LazySingleton.getInstance();
lazy1.sayHello();
System.out.println("同一实例: " + (lazy1 == lazy2));
System.out.println("\n========== 3. 双重检查锁 DCL ==========");
DclSingleton dcl1 = DclSingleton.getInstance();
DclSingleton dcl2 = DclSingleton.getInstance();
dcl1.sayHello();
System.out.println("同一实例: " + (dcl1 == dcl2));
System.out.println("\n========== 4. 静态内部类 ==========");
StaticInnerSingleton inner1 = StaticInnerSingleton.getInstance();
StaticInnerSingleton inner2 = StaticInnerSingleton.getInstance();
inner1.sayHello();
System.out.println("同一实例: " + (inner1 == inner2));
System.out.println("\n========== 5. 枚举单例 ==========");
EnumSingleton.INSTANCE.doSomething();
System.out.println("同一实例: " + (EnumSingleton.INSTANCE == EnumSingleton.INSTANCE));
}
}
执行结果:

✅ 优点:线程安全、防反射、防序列化破坏,代码极简
❌ 缺点:饿汉加载,不支持懒加载
四、单例的破坏方式
- 反射:通过反射调用私有构造器,创建新实例
- 序列化 & 反序列化:反序列化默认生成新对象(枚举除外)
- 多类加载器:不同类加载器会加载多份class,产生多个实例
五、优缺点总结
✅ 优点
- 全局唯一实例,节约系统资源
- 提供统一访问入口,方便管控
- 减少对象创建销毁开销
❌ 缺点
- 单例对象持有引用,容易造成内存泄漏
- 代码耦合,不利于单元测试(难以mock)
- 违背单一职责:既要管理实例,又要实现业务逻辑
- 必须处理多线程、反射、序列化问题
六、框架中的实际应用
- Spring:Bean 默认单例(singleton scope)
- Runtime.getRuntime() JDK运行时对象,典型单例
- MyBatis ErrorContext 使用单例
七、小结
- 饿汉:简单,无懒加载
- DCL:懒加载高性能,注意volatile
- 静态内部类:平衡之选
- 枚举单例:安全性最高,优先推荐(不需要懒加载场景)
0 条笔记