Java 单例模式实现方式
单例模式是确保一个类只有一个实例,并提供一个全局访问点的设计模式。以下是 Java 中实现单例模式的几种常见方式:
1. 饿汉式(Eager Initialization)
public class EagerSingleton {
// 类加载时就初始化
private static final EagerSingleton INSTANCE = new EagerSingleton();
// 私有构造函数
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
特点:
- 线程安全(由JVM类加载机制保证)
- 简单直接
- 可能造成资源浪费(即使不用也会创建实例)
2. 懒汉式(Lazy Initialization,非线程安全)
public class UnsafeLazySingleton {
private static UnsafeLazySingleton instance;
private UnsafeLazySingleton() {}
public static UnsafeLazySingleton getInstance() {
if (instance == null) {
instance = new UnsafeLazySingleton();
}
return instance;
}
}
问题:
- 非线程安全,多线程环境下可能创建多个实例
3. 懒汉式(同步方法,线程安全但效率低)
public class SynchronizedLazySingleton {
private static SynchronizedLazySingleton instance;
private SynchronizedLazySingleton() {}
public static synchronized SynchronizedLazySingleton getInstance() {
if (instance == null) {
instance = new SynchronizedLazySingleton();
}
return instance;
}
}
特点:
- 线程安全
- 每次获取实例都需要同步,性能较差
4. 双重检查锁定(Double-Checked Locking)
public class DCLSingleton {
// 使用volatile禁止指令重排序
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (DCLSingleton.class) {
if (instance == null) { // 第二次检查
instance = new DCLSingleton();
}
}
}
return instance;
}
}
特点:
- 线程安全
- 只有第一次创建时需要同步
- Java 5+ 需要配合 volatile 使用
5. 静态内部类(Holder模式,推荐)
public class HolderSingleton {
private HolderSingleton() {}
private static class SingletonHolder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:
- 线程安全(由JVM类加载机制保证)
- 懒加载(只有调用getInstance()时才加载内部类)
- 无同步开销
- 目前最推荐的方式
6. 枚举实现(Effective Java推荐)
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}
优点:
- 线程安全
- 防止反射攻击
- 防止反序列化重新创建对象
- 代码简洁
- Joshua Bloch在《Effective Java》中推荐的方式
如何选择
- 简单场景:饿汉式或枚举方式
- 需要懒加载:静态内部类方式
- 非常注重性能:双重检查锁定(但要注意正确实现)
- 最佳实践推荐:枚举方式或静态内部类方式
注意事项
- 防止反射攻击:可以在构造函数中添加检查
- 防止反序列化:实现
readResolve()
方法 - 在分布式/集群环境中,这些单例实现仅适用于单个JVM