创建型-单例模式

60 阅读3分钟

导读

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类在整个应用程序生命周期中只有一个实例存在,并提供一个全局访问点来获取该实例。

在 Java 中,常见的实现方式主要有两种:饿汉式(Eager Initialization)懒汉式(Lazy Initialization)。它们各有优缺点,适用于不同场景。

实现方式是否延迟加载是否线程安全是否推荐
饿汉式❌ 否✅ 是✅ 推荐(简单场景)
懒汉式(无锁)✅ 是❌ 否❌ 不推荐
懒汉式(synchronized)✅ 是✅ 是⚠️ 可接受(性能略差)
双重检查锁定✅ 是✅ 是✅ 推荐
静态内部类✅ 是✅ 是✅ 推荐
枚举✅ 是✅ 是✅✅ 最推荐

🎯 应用场景

  • 数据库连接池管理
  • 日志记录器(如 Logger
  • 配置管理类(如 Properties
  • Spring 中的 Bean 默认就是单例模式

一、饿汉式(Eager Initialization)

特点:类加载时立即创建实例(急切初始化)
优点:线程安全,无需加锁(由 JVM 类加载机制保证)
缺点:可能造成资源浪费(未使用时也占用内存)

1. 基础实现(静态变量)

public class EagerSingleton {
    // 类加载时初始化实例
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {}  // 私有构造器
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

2. 静态代码块变体

public class EagerSingleton {
    private static final EagerSingleton INSTANCE;
    
    static {
        try {
            INSTANCE = new EagerSingleton();
        } catch (Exception e) {
            throw new RuntimeException("初始化失败", e);
        }
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

3. 枚举实现(终极方案)

public enum EnumSingleton {
    INSTANCE;  // 单例实例
    
    public void doSomething() {
        // 业务方法
    }
}
// 使用:EnumSingleton.INSTANCE.doSomething();

优势

  • 绝对防止反射攻击
  • 自动处理序列化/反序列化
  • 线程安全
  • 代码最简洁(Java 官方推荐方式)

二、懒汉式(Lazy Initialization)

特点:首次使用时创建实例(延迟初始化)
优点:节省资源
缺点:需额外处理线程安全问题

1. 非线程安全基础版

public class UnsafeLazySingleton {
    private static UnsafeLazySingleton instance;
    
    private UnsafeLazySingleton() {}
    
    public static UnsafeLazySingleton getInstance() {
        if (instance == null) {  // 线程不安全点
            instance = new UnsafeLazySingleton();
        }
        return instance;
    }
}

2. 线程安全同步方法版(低效)

public class SynchronizedLazySingleton {
    private static SynchronizedLazySingleton instance;
    
    private SynchronizedLazySingleton() {}
    
    public static synchronized SynchronizedLazySingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedLazySingleton();
        }
        return instance;
    }
}

缺点:每次获取实例都同步,性能差

3. 双重检查锁定(DCL)版(高效线程安全)

public class DoubleCheckedLockingSingleton {
    // volatile 禁止指令重排序
    private static volatile DoubleCheckedLockingSingleton instance;
    
    private DoubleCheckedLockingSingleton() {}
    
    public static DoubleCheckedLockingSingleton getInstance() {
        if (instance == null) {  // 第一次检查(无锁)
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (instance == null) {  // 第二次检查(有锁)
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }
}

关键点

  • volatile 防止 JVM 指令重排序(避免返回未初始化的对象)
  • 两次判空确保单例

4. 静态内部类(Holder)模式(推荐)

public class HolderSingleton {
    private HolderSingleton() {}
    
    private static class Holder {
        // 由 JVM 保证线程安全初始化
        static final HolderSingleton INSTANCE = new HolderSingleton();
    }
    
    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;  // 首次调用时加载 Holder 类
    }
}

优势

  • 线程安全(JVM 类加载机制)
  • 懒加载(调用 getInstance() 时才初始化)
  • 无同步开销

五、选型建议

  1. 首选枚举方案(Java 5+)

    • 满足所有安全需求
    • 代码简洁明了(《Effective Java》推荐)
  2. 需要懒加载时

    • 静态内部类(平衡安全性与性能)
    • 避免使用基础同步方法(性能差)
  3. 明确需要饿加载

    • 简单场景用饿汉式
    • 需异常处理时用静态块变体
  4. 超高性能场景

    • 首选枚举饿汉式
    • 次选双重检查锁定(注意 volatile)

📌 设计原则:在满足需求的前提下,选择最简单的实现。单例模式已被认为是一种"反模式",过度使用会导致代码耦合度高、难以测试,建议结合依赖注入框架(如Spring)管理单例。