问题
什么是单例模式,如何实现一个单例?
答案
1. 核心概念
单例模式(Singleton Pattern) 是一种创建型设计模式,确保一个类在整个应用程序生命周期中只有一个实例,并提供全局访问点。
典型应用场景:
- 数据库连接池、线程池
- 配置管理器、日志管理器
- Spring 中的 Bean 默认是单例
- 系统全局缓存、计数器
2. 实现方式与原理
方式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:懒汉式同步方法(线程安全但低效)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
特点:
- ✅ 线程安全
- ❌ 性能差(每次调用都要同步)
方式4:双重检查锁(推荐)
public class Singleton {
// volatile 防止指令重排序
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 SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
特点:
- ✅ 延迟加载(调用
getInstance()时才加载内部类) - ✅ 线程安全(类加载机制保证)
- ✅ 性能好(无同步开销)
- 最佳实践之一
方式6:枚举(最安全,推荐)
public enum Singleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}
特点:
- ✅ 天然线程安全
- ✅ 防止反序列化创建新实例
- ✅ 防止反射攻击
- 《Effective Java》推荐方式
3. 线程安全与性能考量
| 实现方式 | 线程安全 | 延迟加载 | 性能 | 推荐指数 |
|---|---|---|---|---|
| 饿汉式 | ✅ | ❌ | ⭐⭐⭐ | ⭐⭐⭐ |
| 懒汉式(不同步) | ❌ | ✅ | ⭐⭐⭐ | ❌ |
| 懒汉式(同步方法) | ✅ | ✅ | ⭐ | ⭐ |
| 双重检查锁 | ✅ | ✅ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 枚举 | ✅ | ❌ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
注意事项:
- 防反射攻击:在构造方法中加入判断,枚举天然防护
- 防序列化破坏:实现
readResolve()方法或使用枚举 - 分布式环境:JVM 级别单例无法保证分布式唯一性,需要分布式锁或注册中心
4. 答题总结
简洁回答模板:
“单例模式确保类只有一个实例并提供全局访问点。常用于线程池、配置管理等场景。
主要实现方式:
- 饿汉式:类加载时创建,线程安全但无延迟加载
- 双重检查锁:需要 volatile 防止指令重排序
- 静态内部类:利用类加载机制,兼顾延迟加载和线程安全
- 枚举:最安全,防反射和反序列化
推荐使用静态内部类或枚举实现,前者写法优雅且高效,后者最安全。”