在 Java 中,单例模式是一种确保一个类在整个应用中只有一个实例的设计模式。实现单例模式时,线程安全性是一个重要的考虑因素,尤其是在多线程环境下。
单例模式的线程安全问题
在多线程环境中,如果多个线程同时试图创建实例,可能会导致:
- 多个实例问题:线程同时访问,可能会创建多个实例。
- 数据一致性问题:非线程安全的操作可能破坏单例模式的设计初衷。
线程安全的单例模式实现方式
1. 饿汉式(线程安全)
- 实现:类加载时就创建单例实例。
- 线程安全性:由于实例在类加载时就初始化,
JVM
会保证线程安全。
- 优点:简单,线程安全。
- 缺点:如果实例没有被使用,会浪费内存。
2. 懒汉式(线程不安全)
- 实现:实例在第一次需要时创建。
- 线程安全性:在多线程下,可能会创建多个实例。
- 解决方案:加同步锁(见下文)。
3. 懒汉式(同步方法,线程安全)
- 实现:通过
synchronized
关键字确保线程安全。
- 线程安全性:线程安全,但性能较差。
- 缺点:每次访问方法都需要获取锁,开销较大。
4. 双重检查锁(推荐)
- 实现:结合同步块和双重检查,减少锁的开销。
- 线程安全性:线程安全,性能较好。
- 关键点:需要使用
volatile
关键字,避免指令重排序。
5. 静态内部类(线程安全,推荐)
- 实现:利用类加载机制,延迟初始化单例实例。
- 线程安全性:JVM 保证类加载的线程安全性。
- 优点:懒加载、高效、线程安全。
6. 枚举单例(线程安全,推荐)
- 实现:利用枚举类的特性,天然支持序列化和线程安全。
- 线程安全性:线程安全且防止反序列化创建新实例。
单例模式线程安全性的选择建议
- 推荐方式:
- 小型项目或简单需求:直接使用饿汉式。
- 需要延迟加载且高并发场景:使用双重检查锁或静态内部类。
- 需要最简单且防止反序列化破坏单例:使用枚举单例。
- 避免的方式: