在 Java 中,单例模式(Singleton)的常见实现方式有以下几种,每种方式都有其特点和适用场景:
1. 饿汉式(Eager Initialization)
在类加载时直接初始化实例,线程安全但可能浪费资源(如果实例未被使用)。
public class EagerSingleton { private static final EagerSingleton INSTANCE = new EagerSingleton(); private EagerSingleton() {} // 私有构造器 public static EagerSingleton getInstance() { return INSTANCE; } }
优点:实现简单,线程安全。
缺点:实例在类加载时创建,可能占用不必要的资源。
2. 懒汉式(Lazy Initialization,非线程安全)
延迟实例化,但非线程安全,多线程环境下可能创建多个实例。
public class LazySingleton { private static LazySingleton instance; private LazySingleton() {} public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
优点:延迟加载,节省资源。
缺点:非线程安全。
3. 懒汉式(线程安全版,synchronized方法)
通过 `synchronized` 方法保证线程安全,但性能较差。
public class SynchronizedSingleton { private static SynchronizedSingleton instance; private SynchronizedSingleton() {} public static synchronized SynchronizedSingleton getInstance() { if (instance == null) { instance = new SynchronizedSingleton(); } return instance; } }
优点:线程安全,延迟加载。
缺点:每次获取实例都需同步,性能开销大。
4. 双重检查锁(Double-Checked Locking)
结合 `synchronized` 块和 `volatile` 关键字,兼顾线程安全和性能。
public class DCLSingleton { 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; } }
优点:线程安全,延迟加载,性能较好。
缺点:实现稍复杂,需注意 `volatile` 的使用。
5. 静态内部类(Static Inner Class)
利用类加载机制保证线程安全,延迟加载且无同步开销。
public class InnerClassSingleton { private InnerClassSingleton() {} private static class SingletonHolder { private static final InnerClassSingleton INSTANCE = new InnerClassSingleton(); } public static InnerClassSingleton getInstance() { return SingletonHolder.INSTANCE; } }
优点:线程安全,延迟加载,无需同步。
缺点:无法通过参数初始化实例。
6. 枚举(Enum Singleton)
通过枚举实现,天然防止反射攻击和序列化问题,是《Effective Java》推荐的方式。
public enum EnumSingleton { INSTANCE; // 单例实例 // 可以添加其他成员变量和方法 private String value; // 私有构造器(枚举的构造器默认是私有的,可以省略 private) EnumSingleton() { value = "Initial Value"; } // 获取单例实例 public static EnumSingleton getInstance() { return INSTANCE; } // 业务方法 public void doSomething() { System.out.println("Doing something with value: " + value); } // 设置值 public void setValue(String value) { this.value = value; } // 获取值 public String getValue() { return value; } }
使用枚举单例
public class Main { public static void main(String[] args) { // 获取单例实例 EnumSingleton singleton = EnumSingleton.getInstance(); // 调用业务方法 singleton.doSomething(); // 修改值 singleton.setValue("New Value"); // 再次调用业务方法 singleton.doSomething(); // 验证单例 EnumSingleton anotherInstance = EnumSingleton.getInstance(); System.out.println("Is singleton the same instance? " + (singleton == anotherInstance)); } }
枚举单例的优点
线程安全:枚举实例的创建由 JVM 保证,天然线程安全。
防止反射攻击:枚举类型不能通过反射创建实例,避免了反射破坏单例。
防止序列化破坏:枚举单例在序列化和反序列化时不会创建新的实例。
简洁易读:代码简洁,无需额外处理线程安全、反射和序列化问题。
枚举单例的缺点
无法继承:枚举类型不能继承其他类(但可以实现接口)。
不够灵活:枚举单例的实例在类加载时创建,无法延迟加载
7. 注意事项
- 反射攻击:除枚举外,其他方式需在构造器中防止重复创建(抛出异常)。
- 序列化:非枚举单例需实现 `readResolve()` 方法防止反序列化生成新实例。
0条评论
点击登录参与评论