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

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并发编程实战》(Brian Goetz等);枚举方案出自《Effective Java》(Joshua Bloch);类加载机制依据JVM规范(Oracle官方文档)。

0