单例类
单例类(Singleton)
单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类在系统中只有一个实例,并提供一个全局访问点。
单例模式的特点
- 唯一性:整个系统中该类只有一个实例。
- 全局访问:可以在整个程序中访问这个实例。
- 延迟初始化(可选):只有在真正需要时才创建实例,提高性能。
单例模式的实现方式
1. 饿汉式(Eager Singleton)
实现方式
- 直接在类加载时创建实例,线程安全。
- 缺点是可能会造成资源浪费(如果实例很早创建,但一直未被使用)。
1 |
|
2. 懒汉式(Lazy Singleton)
实现方式
- 只有在第一次调用
getInstance()
时才创建实例,避免资源浪费。 - 但非线程安全,多个线程可能同时创建多个实例。
1 |
|
问题:多线程环境下,可能出现多个实例。
3. 线程安全的懒汉式
方式 1:使用 synchronized
1 |
|
缺点:synchronized
影响性能,每次访问都需要加锁。
方式 2:双重检查锁(Double-Check Locking)
1 |
|
volatile
关键字:防止指令重排,保证可见性。- 双重检查:避免不必要的加锁,提高性能。
4. 静态内部类(推荐方式)
1 |
|
优点:
- 线程安全,利用类加载机制确保实例唯一。
- 延迟加载(Lazy Loading),只有
getInstance()
被调用时,才创建实例。
5. 枚举实现单例(最佳方案)
1 |
|
优点:
- 线程安全,由 JVM 保证。
- 防止反射攻击(私有构造方法仍可通过反射破坏单例,但枚举不会)。
- 防止反序列化创建新实例。
防止反射和序列化破坏单例
防止反射破坏单例
- 在构造方法中检测实例是否已存在:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class Singleton {
private static volatile Singleton instance;
private Singleton() {
if (instance != null) {
throw new RuntimeException("禁止反射创建单例!");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}防止序列化破坏单例
- 通过
readResolve()
方法确保反序列化返回同一个实例:
1
2
3private Object readResolve() {
return instance;
}- 通过
总结
方式 | 是否线程安全 | 是否延迟加载 | 是否推荐 |
---|---|---|---|
饿汉式 | ✅ | ❌ | ❌(可能浪费资源) |
懒汉式 | ❌ | ✅ | ❌(非线程安全) |
synchronized 懒汉式 | ✅ | ✅ | ❌(影响性能) |
双重检查锁 | ✅ | ✅ | ✅ |
静态内部类 | ✅ | ✅ | ✅(推荐) |
枚举 | ✅ | ✅ | ⭐(最佳方式) |
最佳实践:优先使用 枚举 或 静态内部类 实现单例。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Firefly!