单例模式八种实现
单例模式是最简单的设计模式,但实现方式多样,每种都有其适用场景。
一、什么是单例模式
单例模式 = 一个类只有一个实例 + 提供全局访问点
应用场景:
- 配置管理器
- 数据库连接池
- 日志记录器
- 线程池
二、八种实现方式
2.1 饿汉式(静态常量)
public class Singleton {
// 类加载时创建
private static final Singleton INSTANCE = new Singleton();
// 私有构造
private Singleton() {}
// 全局访问点
public static Singleton getInstance() {
return INSTANCE;
}
}
优点:
- 线程安全(类加载保证)
- 实现简单
缺点:
- 可能浪费资源(不用也创建)
2.2 饿汉式(静态代码块)
public class Singleton {
private static final Singleton INSTANCE;
static {
INSTANCE = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
特点:与方式 1 类似,可在代码块中处理异常
2.3 懒汉式(线程不安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 线程不安全
}
return instance;
}
}
问题:多线程可能创建多个实例
2.4 懒汉式(线程安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:线程安全
缺点:性能低(每次都同步)
2.5 双重检查锁定(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防止指令重排- 两次 null 检查
2.6 静态内部类
public class Singleton {
private Singleton() {}
// 静态内部类
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
优点:
- 线程安全(类加载机制)
- 延迟加载(用时才加载内部类)
- 无需 volatile
推荐:⭐⭐⭐⭐⭐
2.7 枚举(最简洁)
public enum Singleton {
INSTANCE;
// 可以添加方法
public void doSomething() {
// ...
}
}
// 使用
Singleton.INSTANCE.doSomething();
优点:
- 线程安全(枚举保证)
- 防止反射攻击
- 防止序列化破坏
- 代码简洁
推荐:⭐⭐⭐⭐⭐
2.8 注册表(容器)
public class SingletonRegistry {
private static Map<String, Object> registry = new ConcurrentHashMap<>();
@SuppressWarnings("unchecked")
public static <T> T getInstance(String name, Class<T> clazz) {
return (T) registry.computeIfAbsent(name, key -> {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
适用场景:需要管理多个单例
三、对比总结
| 方式 | 线程安全 | 延迟加载 | 性能 | 推荐度 |
|---|---|---|---|---|
| 饿汉式 | ✅ | ❌ | 高 | ⭐⭐⭐ |
| 懒汉式 | ✅ | ✅ | 低 | ⭐⭐ |
| DCL | ✅ | ✅ | 高 | ⭐⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | 高 | ⭐⭐⭐⭐⭐ |
| 枚举 | ✅ | ✅ | 高 | ⭐⭐⭐⭐⭐ |
四、单例破坏与防护
4.1 反射攻击
// 攻击普通单例
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance = constructor.newInstance(); // 创建新实例
// 防护(枚举)
// 枚举的构造器不能被反射调用
4.2 序列化攻击
// 攻击
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj"));
oos.writeObject(Singleton.getInstance());
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj"));
Singleton instance = (Singleton) ois.readObject(); // 新实例
// 防护
public class Singleton implements Serializable {
// ...
// 防止序列化破坏
private Object readResolve() {
return INSTANCE;
}
}
4.3 克隆攻击
// 攻击
Singleton clone = (Singleton) instance.clone();
// 防护
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton cannot be cloned");
}
五、最佳实践
5.1 推荐使用枚举
// 最简洁、最安全
public enum Singleton {
INSTANCE;
private final DataSource dataSource;
Singleton() {
dataSource = createDataSource();
}
public DataSource getDataSource() {
return dataSource;
}
}
5.2 次选静态内部类
// 适用于需要继承的场景
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
5.3 避免使用懒汉式
// ❌ 不推荐
public static synchronized Singleton getInstance() {
// 性能低
}
// ✅ 推荐
public static Singleton getInstance() {
return Holder.INSTANCE;
}
六、Spring 中的单例
6.1 Spring 默认单例
// Spring Bean 默认单例
@Component
public class UserService {
// Spring 管理,默认单例
}
// 配置作用域
@Scope("prototype") // 多例
@Scope("request") // 请求级别
@Scope("session") // 会话级别
6.2 Spring 单例 vs 单例模式
Spring 单例:
- 容器管理
- 可配置作用域
- 支持依赖注入
单例模式:
- 代码实现
- 固定单例
- 手动管理依赖
七、总结
单例模式核心要点:
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 枚举 | 简洁、安全 | 不能继承 | 首选推荐 |
| 静态内部类 | 延迟加载、安全 | 代码稍多 | 次选推荐 |
| DCL | 性能好 | 实现复杂 | 特殊场景 |
| 饿汉式 | 简单 | 不延迟加载 | 资源占用小 |
推荐使用枚举实现单例,简洁且安全,有效防止反射和序列化攻击。