【设计模式】单例模式

单例模式深度解析:从基础到实现原理

一、单例模式核心概念

单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供该实例的全局访问点。这种模式的核心价值在于:

  • ✅ 避免资源冲突(如配置文件、数据库连接池)
  • ✅ 节省系统资源开销
  • ✅ 统一管理共享资源
  • ✅ 控制全局访问点

典型应用场景

  1. 配置管理器(全局共享配置)
  2. 数据库连接池(避免重复创建连接)
  3. 日志记录器(统一管理日志写入)
  4. 设备驱动程序(如打印机控制)
  5. 缓存系统(全局共享缓存数据)

二、单例模式实现方式大全

1. 饿汉式(线程安全)
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {
        // 防止反射攻击
        if (INSTANCE != null) {
            throw new IllegalStateException("Singleton already initialized");
        }
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}
  • ⚡ 优点:实现简单,线程安全
  • ⚠️ 缺点:类加载时即初始化,可能造成资源浪费
2. 懒汉式(非线程安全)
public class LazySingleton {
    private static LazySingleton instance;
    
    private LazySingleton() {}
    
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
  • ⚡ 优点:延迟初始化
  • ⚠️ 缺点:多线程环境下不安全
3. 同步方法懒汉式(线程安全)
public class SynchronizedSingleton {
    private static SynchronizedSingleton instance;
    
    private SynchronizedSingleton() {}
    
    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}
  • ⚡ 优点:线程安全
  • ⚠️ 缺点:每次获取实例都加锁,性能差
4. 双重检查锁(DCL)
public class DoubleCheckedLockingSingleton {
    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防止指令重排序
5. 静态内部类(推荐)
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}
    
    private static class Holder {
        static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
    
    public static StaticInnerClassSingleton getInstance() {
        return Holder.INSTANCE;
    }
    
    // 防止反序列化破坏单例
    protected Object readResolve() {
        return getInstance();
    }
}
6. 枚举单例(最佳实践)
public enum EnumSingleton {
    INSTANCE;
    
    // 添加业务方法
    public void businessMethod() {
        System.out.println("Business logic executed");
    }
}

三、静态内部类原理深度剖析

静态内部类实现之所以无需同步开销,关键在于利用了Java类加载机制的天然线程安全性:

🔍 核心机制:JVM的类初始化锁

Java虚拟机规范明确规定:

每个类都有唯一的初始化锁。类初始化由JVM隐式加锁,确保多线程环境下只执行一次初始化。

实现原理

public class Singleton {
    private Singleton() {}
    
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return Holder.INSTANCE; // 触发类加载
    }
}
✨ 无同步开销的底层原理
  1. 延迟加载时机
    Holder类在首次调用getInstance()时才会被加载

  2. 类初始化锁机制

    步骤多线程场景JVM行为
    线程A首次调用getInstance()触发Holder类加载JVM获取类初始化锁
    线程B同时调用getInstance()检测到Holder正在初始化线程B阻塞
    线程A完成初始化释放锁
    线程B被唤醒发现类已初始化直接返回实例
  3. 字节码验证
    getInstance()方法的字节码中没有任何monitorenter/monitorexit指令(无同步锁操作)

⚖️ 同步机制对比
实现方式同步机制性能影响
同步方法懒汉式方法级synchronized每次调用都加锁
双重检查锁代码块synchronized首次创建后仍需读volatile
静态内部类JVM类初始化锁仅首次加载时隐式同步
🛡️ JVM如何保证线程安全

根据Java语言规范(JLS §12.4.2):

类初始化阶段执行原子操作:

  1. 获取类初始化锁
  2. 如果类正在初始化,则阻塞当前线程
  3. 如果类未初始化,执行静态初始化
  4. 释放锁并通知等待线程

四、单例模式防护措施

1. 反射攻击防护
private Singleton() {
    if (instance != null) {
        throw new IllegalStateException("Singleton already initialized");
    }
}
2. 反序列化防护
// 在类中添加readResolve方法
protected Object readResolve() {
    return getInstance();
}

唯一完全防护方案:使用枚举单例(天然防反射和反序列化攻击)

五、实现方式对比与最佳实践

实现方式线程安全延迟加载防反射防序列化性能
饿汉式⭐⭐⭐⭐
懒汉式⭐⭐⭐⭐
同步方法⭐⭐
双重检查锁⭐⭐⭐
静态内部类⭐⭐⭐⭐
枚举⭐⭐⭐⭐

最佳实践建议

  1. 优先选择枚举实现 - 简洁安全,满足大多数场景
  2. 需要延迟加载时选择静态内部类 - 平衡性能和安全性
  3. 避免使用双重检查锁 - 除非明确理解内存模型细节
  4. 谨慎使用单例 - 过度使用会导致代码耦合度高

六、总结

单例模式通过控制实例化过程,为系统提供了统一的访问入口,是管理共享资源的利器。关键要点:

  1. 静态内部类实现利用JVM类加载机制实现无锁线程安全
  2. 枚举实现提供最全面的防护(反射+序列化)
  3. 所有实现都需考虑反射和序列化的防护
  4. 根据实际需求在安全性和性能间取得平衡

📌 设计警示:单例模式虽好,但不要滥用。在需要真正全局唯一实例时才使用,否则会增加系统耦合度和测试难度。

需要单例吗?

需要延迟加载?

需要防御反射/序列化?

使用枚举实现

使用静态内部类

实现完成

通过深入理解各种实现方式的原理和适用场景,开发者可以做出更明智的设计决策,构建出既安全又高效的系统架构。

原创作者: sun-10387834 转载于: https://www.cnblogs.com/sun-10387834/p/18950201
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值