单例模式确保一个类只有一个实例,并提供全局访问点。核心原理:
- 私有化构造方法(禁止外部创建实例)
- 静态私有成员(持有唯一实例)
- 静态公有方法(提供全局访问入口)
一、实现方式对比
实现方式 | 线程安全 | 懒加载 | 防反射/序列化 | 代码复杂度 | 推荐指数 |
---|---|---|---|---|---|
饿汉式 | ✅ | ❌ | ❌ | ⭐ | ★★☆☆☆ |
懒汉式(同步方法) | ✅ | ✅ | ❌ | ⭐⭐ | ★★☆☆☆ |
双重检查锁(DCL) | ✅ | ✅ | ❌ | ⭐⭐⭐ | ★★★☆☆ |
静态内部类 | ✅ | ✅ | ❌ | ⭐⭐ | ★★★★☆ |
枚举 | ✅ | ❌ | ✅ | ⭐ | ★★★★★ |
二、具体实现及原理
1. 饿汉式(Eager Initialization)
java
public class EagerSingleton {
// 类加载时立即初始化(线程安全由JVM保证)
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {} // 私有构造
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
- 优点:实现简单,线程安全
- 缺点:非懒加载,可能造成资源浪费
2. 懒汉式(Lazy Initialization - 同步方法)
java
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 方法同步保证线程安全(性能瓶颈)
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
- 优点:懒加载
- 缺点:同步方法性能差(每次调用都加锁)
3. 双重检查锁(Double-Checked Locking)
java
public class DCLSingleton {
// volatile禁止指令重排序(防止返回未初始化的对象)
private volatile static DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) { // 第一次检查(避免不必要的锁)
synchronized (DCLSingleton.class) {
if (instance == null) { // 第二次检查(确保唯一性)
instance = new DCLSingleton();
}
}
}
return instance;
}
}
- 关键点 :
volatile
+ 两次判空 + 同步块 - 解决隐患:避免因指令重排序导致返回未初始化的对象
4. 静态内部类(Holder)
java
public class HolderSingleton {
private HolderSingleton() {}
// 静态内部类在首次使用时加载
private static class Holder {
static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return Holder.INSTANCE; // 触发类加载(线程安全由JVM保证)
}
}
- 原理 :利用JVM类加载机制(静态内部类在调用
getInstance()
时才加载) - 优势:天然线程安全 + 懒加载 + 无锁高性能
5. 枚举(Enum - 最佳实践)
java
public enum EnumSingleton {
INSTANCE; // 单例实例
// 示例方法
public void doSomething() {
System.out.println("Singleton working");
}
}
- 使用方式 :
EnumSingleton.INSTANCE.doSomething();
- 优势 :
- 绝对单例(JVM保证)
- 自动防反射攻击
- 自动防序列化破坏
- 代码最简洁
三、常见问题解决方案
1. 反射攻击防护
java
private Singleton() {
// 防止通过反射创建新实例
if (instance != null) {
throw new IllegalStateException("Singleton already initialized");
}
}
(枚举天然防御反射,无需额外处理)
2. 序列化破坏防护
java
// 在非枚举实现中添加此方法
protected Object readResolve() {
return getInstance(); // 反序列化时返回现有实例
}
四、总结建议
- 首选枚举:满足所有单例需求(安全、简洁、防破坏)
- 需要懒加载时:用静态内部类
- 旧版本JDK(<1.5):用双重检查锁 (需确保
volatile
正确使用) - 避免饿汉式和同步方法懒汉式(存在明显缺陷)