单例模式 (Singleton Pattern)
外观
单例模式 (Singleton Pattern)[编辑 | 编辑源代码]
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这种模式常用于需要控制资源访问的场景,如数据库连接、日志记录器或配置管理器。
设计意图[编辑 | 编辑源代码]
单例模式的核心目标是:
- 保证一个类仅有一个实例
- 提供该实例的全局访问点
- 控制实例化过程(通常延迟初始化)
实现方式[编辑 | 编辑源代码]
基础实现(非线程安全)[编辑 | 编辑源代码]
最简单的单例实现方式如下,但仅适用于单线程环境:
public class Singleton {
private static Singleton instance;
// 私有构造函数防止外部实例化
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
问题:多线程环境下可能创建多个实例。
线程安全实现[编辑 | 编辑源代码]
方法1:同步方法[编辑 | 编辑源代码]
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
缺点:每次获取实例都需要同步,性能较差。
方法2:双重检查锁定[编辑 | 编辑源代码]
更高效的线程安全实现:
public class Singleton {
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;
}
}
关键点:
volatile
防止指令重排序- 双重检查减少同步开销
方法3:静态内部类[编辑 | 编辑源代码]
利用类加载机制保证线程安全:
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
优点:线程安全且实现简洁,延迟初始化。
方法4:枚举实现[编辑 | 编辑源代码]
Java最佳实践方式:
public enum Singleton {
INSTANCE;
public void doSomething() {
// 实例方法
}
}
优势:
- 绝对防止多次实例化
- 自动支持序列化
- 线程安全
类图[编辑 | 编辑源代码]
实际应用场景[编辑 | 编辑源代码]
案例1:数据库连接池[编辑 | 编辑源代码]
大多数应用只需要一个数据库连接池实例来管理所有数据库连接:
public class DatabasePool {
private static DatabasePool instance;
private List<Connection> connections;
private DatabasePool() {
// 初始化连接池
}
public static synchronized DatabasePool getInstance() {
if (instance == null) {
instance = new DatabasePool();
}
return instance;
}
public Connection getConnection() {
// 获取连接逻辑
}
}
案例2:应用配置管理[编辑 | 编辑源代码]
全局配置只需要加载一次:
class AppConfig:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.load_config()
return cls._instance
def load_config(self):
# 从文件加载配置
pass
优缺点分析[编辑 | 编辑源代码]
优点 | 缺点 |
---|---|
控制实例数量,节约系统资源 | 违反单一职责原则(同时控制实例化和业务逻辑) |
全局访问点方便管理 | 难以扩展(通常不允许子类化) |
延迟初始化优化性能 | 多线程环境需要特殊处理 |
避免全局变量污染命名空间 | 单元测试困难(难以模拟替换) |
数学原理[编辑 | 编辑源代码]
单例模式可以看作是对集合基数的约束:
其中是实例集合。当实现完全正确时:
常见问题[编辑 | 编辑源代码]
如何防止反射攻击?[编辑 | 编辑源代码]
在构造函数中添加检查:
private Singleton() {
if (instance != null) {
throw new IllegalStateException("Already initialized");
}
}
如何防止反序列化创建新实例?[编辑 | 编辑源代码]
实现readResolve()
方法:
protected Object readResolve() {
return getInstance();
}
何时应该使用单例?[编辑 | 编辑源代码]
- 当系统需要且只需要一个实例时
- 当需要严格控制全局访问点时
- 当实例需要延迟初始化时
替代方案[编辑 | 编辑源代码]
对于需要灵活控制实例数量的场景,可以考虑:
- 依赖注入容器
- 静态工具类(无状态时)
- 工厂模式控制实例化
总结[编辑 | 编辑源代码]
单例模式是设计模式中最简单但实现最微妙的一种。正确实现需要考虑线程安全、序列化、反射等问题。在Java中,枚举实现是最安全的方式;其他语言也都有各自的实现最佳实践。使用时需权衡其便利性与对代码可测试性的影响。