第 1 天:单例模式(Singleton Pattern)------ 创建型模式
1. 核心定义
单例模式确保一个类仅有一个实例 ,并提供一个全局访问点来获取该实例。它的核心是 "控制实例数量",避免重复创建消耗资源的对象(如数据库连接池、配置管理器)。
2. 解决的核心问题
- 避免多个实例导致的资源浪费(如频繁创建数据库连接);
- 防止多个实例引发的数据不一致(如配置文件读取实例,多实例可能导致配置冲突);
- 提供统一的全局访问入口,简化对象调用。
3. 关键设计要点
- 私有构造函数 :禁止外部通过
new
关键字创建实例; - 私有静态成员变量:存储类的唯一实例;
- 公有静态方法:作为全局访问点,返回唯一实例(需处理线程安全问题)。
4. 常见实现方式(Java 示例)
方式 1:饿汉式(线程安全,简单但可能浪费资源)
"饿汉式" 在类加载时就创建实例,避免线程安全问题,但如果实例未被使用,会提前占用内存。
csharp
public class HungrySingleton {
// 1. 私有静态成员变量:类加载时创建唯一实例
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 2. 私有构造函数:禁止外部new
private HungrySingleton() {}
// 3. 公有静态方法:返回实例
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
方式 2:懒汉式(线程不安全→线程安全优化)
"懒汉式" 在首次调用 getInstance()
时才创建实例,节省内存,但需额外处理线程安全(否则多线程下可能创建多个实例)。
线程安全优化版(双重检查锁定) :
csharp
public class LazySingleton {
// 1. 私有静态成员变量:volatile防止指令重排序(关键!)
private static volatile LazySingleton INSTANCE;
// 2. 私有构造函数
private LazySingleton() {}
// 3. 公有静态方法:双重检查锁定
public static LazySingleton getInstance() {
// 第一次检查:避免已创建实例时的锁竞争
if (INSTANCE == null) {
synchronized (LazySingleton.class) {
// 第二次检查:防止多线程同时进入第一个检查后重复创建
if (INSTANCE == null) {
INSTANCE = new LazySingleton();
}
}
}
return INSTANCE;
}
}
方式 3:静态内部类(推荐,线程安全且延迟加载)
利用 Java "静态内部类加载时机" 的特性:内部类在被调用时才加载,既实现延迟加载,又天然避免线程安全问题(类加载过程由 JVM 保证线程安全)。
csharp
public class StaticInnerClassSingleton {
// 1. 私有构造函数
private StaticInnerClassSingleton() {}
// 2. 静态内部类:存储唯一实例
private static class SingletonHolder {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
// 3. 公有静态方法:调用内部类的实例
public static StaticInnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5. 应用场景
-
系统中只能有一个实例的对象,如:
- 数据库连接池(
DataSource
); - 配置文件管理器(读取全局配置);
- 日志工具类(避免多实例写入日志时的冲突);
- 操作系统的任务管理器、回收站。
- 数据库连接池(
6. 注意事项
- 线程安全:懒汉式必须处理线程安全,否则多线程环境下会破坏 "单例" 特性;
- 序列化问题 :若单例类实现
Serializable
,需重写readResolve()
方法,否则反序列化会创建新实例; - 反射攻击:私有构造函数可通过反射被调用,需在构造函数中添加判断(若实例已存在则抛异常)。