设计模式:单例模式
【摘要】 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。它通过私有化构造函数、自行创建实例和静态方法(如`getInstance()`)实现。适用于数据库连接池、日志管理器等需要全局唯一对象的场景。常见的实现方式包括饿汉式、懒汉式、双重检查锁、静态内部类和枚举。线程安全问题可通过`synchronized`或双重检查锁解决,同时需防止反射和序列化破坏单例。优点是避免资源浪费,缺点
1. 什么是单例模式?
单例模式是一种 创建型设计模式,确保一个类 只有一个实例,并提供一个 全局访问点 来获取该实例。它的核心目标是控制对象的创建过程,避免资源重复占用或状态不一致。
核心原则:
- 私有化构造函数:禁止外部通过
new
创建实例。 - 自行创建实例:类内部负责创建唯一实例。
- 全局访问点:通过静态方法(如
getInstance()
)提供实例访问。
2. 应用场景
单例模式适用于需要 全局唯一对象 的场景,例如:
- 数据库连接池(避免频繁创建连接)
- 日志管理器(统一记录日志)
- 配置信息类(全局共享配置)
- 线程池、缓存系统、硬件驱动访问等
3. 单例模式的实现方式
3.1 饿汉式(Eager Initialization)
- 特点:类加载时就创建实例,线程安全但可能浪费资源。
- 代码示例:
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {} // 私有构造函数
public static Singleton getInstance() {
return instance;
}
}
3.2 懒汉式(Lazy Initialization)
- 特点:延迟实例化,首次调用
getInstance()
时创建实例。 - 问题:线程不安全(多线程可能创建多个实例)。
- 代码示例:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.3 线程安全的懒汉式(加锁)
- 特点:通过
synchronized
关键字保证线程安全,但性能较低。 - 代码示例:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.4 双重检查锁(Double-Checked Locking)
- 特点:减少锁的粒度,提升性能,同时保证线程安全。
- 代码示例:
public class Singleton {
private static volatile Singleton instance; // volatile 防止指令重排序
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
3.5 静态内部类(Holder)
- 特点:利用类加载机制保证线程安全,且延迟加载。
- 代码示例:
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
3.6 枚举(Enum)
- 特点:简洁、线程安全、防止反射攻击和序列化问题。
- 代码示例:
public enum Singleton {
INSTANCE; // 单例实例
public void doSomething() {
// 业务方法
}
}
4. 单例模式的线程安全问题
-
根源:多线程环境下,实例可能被多次创建。
-
解决方案:
- 使用
synchronized
或双重检查锁(DCL)。 - 通过静态内部类或枚举实现(推荐)。
- 使用
5. 防止反射和序列化破坏单例
-
反射攻击:通过反射调用私有构造函数创建新实例。
- 解决方法:在构造函数中添加检查逻辑。
-
序列化问题:反序列化时会创建新对象。
- 解决方法:实现
readResolve()
方法返回已有实例。
- 解决方法:实现
示例代码:
public class Singleton implements Serializable {
private static final Singleton instance = new Singleton();
private Singleton() {
if (instance != null) {
throw new IllegalStateException("单例已存在!");
}
}
// 防止反序列化创建新对象
protected Object readResolve() {
return instance;
}
}
6. 单例模式的优缺点
-
优点:
- 全局唯一实例,避免资源浪费。
- 提供统一的访问入口。
-
缺点:
- 违反单一职责原则(类需管理自身实例)。
- 可能隐藏类之间的依赖关系,增加测试难度。
- 长期持有实例可能导致内存泄漏(如 Android 中 Context 单例)。
7. 实际开发中的注意事项
- 优先选择枚举或静态内部类:简洁且线程安全。
- 避免滥用单例:过度使用会导致代码耦合度高。
- 依赖注入框架:如 Spring 默认管理单例 Bean,无需手动实现。
- 单元测试:单例可能增加测试复杂度,可通过 Mock 工具解决。
8. 总结
单例模式的核心是 控制实例数量 和 提供全局访问点。实现时需根据场景选择合适的方式:
- 简单场景:枚举或静态内部类。
- 复杂线程安全需求:双重检查锁。
- 框架开发:结合依赖注入容器(如 Spring)。
正确使用单例模式能优化资源管理,但需警惕其潜在的缺点和误用风险。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)