当前位置:首页 > 后端开发 > 正文

Java单例模式如何正确实现?

单例模式确保类仅有一个实例,并提供全局访问点,常用实现有饿汉式(线程安全但立即加载)、懒汉式(延迟加载需同步锁)、静态内部类(延迟且线程安全)及枚举(最简洁,防反射序列化破坏)。

饿汉式(线程安全)

public class Singleton {
    // 类加载时直接初始化
    private static final Singleton INSTANCE = new Singleton();
    // 私有构造方法防止外部实例化
    private Singleton() {}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
  • 优点:线程安全(JVM类加载机制保证)
  • 缺点:类加载时即创建实例,可能造成资源浪费
  • 适用场景:实例占用资源少且频繁使用

懒汉式(线程不安全版)

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {  // 线程不安全点
            instance = new Singleton();
        }
        return instance;
    }
}
  • 风险:多线程下可能创建多个实例
  • 仅限单线程环境使用

双重检查锁(DCL,线程安全)

public class Singleton {
    private static volatile Singleton instance; // volatile禁止指令重排序
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {                  // 第一次检查
            synchronized (Singleton.class) {     // 加锁
                if (instance == null) {          // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 关键点volatile防止JVM指令重排序(避免返回未初始化的对象)
  • 优点:线程安全且延迟加载
  • 工业级推荐方案

静态内部类(线程安全)

public class Singleton {
    private Singleton() {}
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return Holder.INSTANCE;  // 首次调用时加载内部类
    }
}
  • 原理:利用JVM类加载机制(静态内部类在调用时才加载)
  • 优点:无需同步锁,兼顾线程安全和懒加载

枚举(最佳实践)

public enum Singleton {
    INSTANCE;  // 枚举实例天然单例
    public void doSomething() {
        // 业务方法
    }
}
// 调用方式:Singleton.INSTANCE.doSomething();
  • 优势
    • 绝对线程安全(JVM保证)
    • 防止反射攻击和序列化破坏单例
  • 《Effective Java》作者Joshua Bloch推荐方案

破坏单例的场景及防护

威胁 防护方案
反射攻击 私有构造器中添加throw new RuntimeException()
序列化破坏 实现readResolve()方法返回实例
// 序列化防护示例
protected Object readResolve() {
    return getInstance();
}

总结与选择建议

  1. 简单场景:直接使用饿汉式
  2. 延迟加载需求:优先选择静态内部类枚举
  3. 高并发系统双重检查锁(DCL)
  4. 终极方案枚举(防反射/序列化攻击)

单例模式虽实用,但需注意两点:

Java单例模式如何正确实现?  第1张

  • 过度使用会导致代码耦合度高(违背单一职责原则)
  • 在分布式系统或类加载器不同的环境中可能失效

引用说明
双重检查锁实现参考《Java并发编程实战》(Brian Goetz等);枚举方案出自《Effective Java》(Joshua Bloch);类加载机制依据JVM规范(Oracle官方文档)。

0