单例模式
作用:保证类在 JVM 中只有一个实例,避免重复创建对象,节省资源。
应用场景:
- Spring Bean 默认是单例的
- 数据库连接池(HikariCP、Druid)
- Redis、线程池、日志管理(Logback)
参考代码:单例模式的几种实现
1、懒汉式(线程不安全)
- 特点: 延迟初始化,调用 getInstance() 时才创建实例,但线程不安全。
public class SingletonLazy {
// 静态实例变量,初始为 null
private static SingletonLazy instance;
// 私有构造方法,防止外部实例化
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
// 只有在需要时才创建
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
- 缺点: 多线程环境下,可能会出现多个线程同时进入 if (instance == null),导致创建多个实例,线程不安全。
2、饿汉式(线程安全)
- 特点: 类加载时就创建实例,线程安全,但可能造成资源浪费。
public class SingletonEager {
// 直接初始化
private static final SingletonEager instance = new SingletonEager();
// 私有构造方法
private SingletonEager() {
}
public static SingletonEager getInstance() {
// 直接返回实例
return instance;
}
}
- 缺点: 类加载时即创建实例,即使从未使用,也会占用内存。
3、双重检查锁(DCL,推荐)
- 特点: 线程安全,且避免了资源浪费,是常见的最佳实践。
public class SingletonDCL {
// `volatile` 防止指令重排序
private static volatile SingletonDCL instance;
private SingletonDCL() {
}
public static SingletonDCL getInstance() {
// 先检查实例是否存在
if (instance == null) {
// 线程同步
synchronized (SingletonDCL.class) {
// 二次检查
if (instance == null) {
instance = new SingletonDCL();
}
}
}
return instance;
}
}
- 优点:
- 线程安全,只在第一次创建实例时加锁,提高性能。
- 使用 volatile 防止指令重排,确保 instance 被正确初始化。
4、单例实现对比总结
方式 | 线程安全 | 是否懒加载 | 性能 | 适用场景 |
---|---|---|---|---|
懒汉式 | ❌ 否 | ✅ 是 | ⭐⭐⭐⭐(快但线程不安全) | 单线程环境 |
饿汉式 | ✅ 是 | ❌ 否 | ⭐⭐⭐(加载即创建,资源浪费) | 类加载后立即使用 |
双重检查锁(DCL) | ✅ 是 | ✅ 是 | ⭐⭐⭐⭐⭐(高效安全) | 推荐,通用方案 |
如果你在实际开发中使用单例,DCL(双重检查锁)是最推荐的方式,因为它既保证了线程安全,又避免了资源浪费。