单例模式(Singleton Pattern):让资源类只存在一个实例
✨ 模式简介
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。
常用于控制全局资源的唯一访问入口,例如配置类、日志器、数据库连接池等。
📦 应用场景
- 日志系统(Logger)
- 配置中心(ConfigManager)
- 缓存(Cache)
- 数据库连接池(DataSource)
- 线程池(ThreadPoolExecutor)
- Spring 默认的 Bean 实例就是单例
🧠 核心实现思路
- 构造函数设为
private
,防止外部 new; - 提供一个静态私有变量保存实例;
- 提供一个公共静态方法访问实例。
🧱 单例模式的多种实现方式
1. 饿汉式(线程安全,推荐)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
- 优点:简单,线程安全
- 缺点:类加载即初始化,不管是否使用,可能浪费内存
2. 懒汉式(非线程安全,不推荐)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 缺点:多线程环境下可能创建多个实例,不安全
3. 懒汉式 + synchronized(线程安全但效率低)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 优点:线程安全
- 缺点:每次调用都加锁,性能较差
4. 双重检查锁 DCL(推荐)
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
防止指令重排
5. 静态内部类(最推荐)
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
- 优点:线程安全,延迟加载,写法优雅
6. 枚举实现(最安全)
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Hello Singleton!");
}
}
- 优点:简洁,线程安全,防止反射和序列化破坏
💣 单例模式的破坏与防御
1.反射攻击
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance(); // 破坏单例
防御方法
private static boolean initialized = false;
private Singleton() {
if (initialized) {
throw new RuntimeException("Instance already created!");
}
initialized = true;
}
2.序列化攻击
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj"));
out.writeObject(Singleton.getInstance());
ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj"));
Singleton s = (Singleton) in.readObject(); // 新对象,破坏单例
防御方法
private Object readResolve() {
return getInstance();
}
📊 优缺点总结
优点 | 缺点 |
---|---|
控制实例数量,节省资源 | 全局状态影响测试 |
全局访问点,方便使用 | 依赖隐藏,耦合度上升 |
线程安全(合理实现) | 容易滥用,设计臃肿 |
📚 实际应用
- Spring 容器默认 Bean 是单例作用域
- 日志系统 Logger
- 线程池
- Redis 连接池等
🎯 总结建议
- 单例适合无状态或读多写少资源类
- 不滥用单例,依赖注入优先
- 推荐静态内部类或枚举实现
- 注意线程安全和防御攻击
一个写代码的人,也写字,记录风吹过生活的声音。