单例模式详解

一、什么是单例模式?

单例模式(Singleton Pattern)是一种创建型设计模式 ,核心思想是:
保证一个类在整个程序运行期间,有且仅有一个实例,并提供一个全局访问点来获取这个唯一实例。

核心特点

  1. 唯一实例:类只能被实例化一次。
  2. 全局访问:提供一个静态方法,让外部能方便地获取这个唯一实例。
  3. 私有构造 :构造方法私有化,禁止外部通过 new 关键字创建实例。

适用场景

  • 配置类(如数据库连接配置、系统配置)
  • 日志类(全局统一日志输出)
  • 线程池、缓存、连接池等需要共享资源的场景

二、单例模式的实现方式

单例模式有多种实现方式,各有优缺点,下面是常见的几种:

1. 饿汉式(Eager Initialization)

特点:类加载时就创建实例,线程安全,但可能造成资源浪费(如果实例一直没被使用)。

java 复制代码
public class Singleton {
    // 类加载时就初始化
    private static final Singleton instance = new Singleton();
    
    // 私有构造
    private Singleton() {}
    
    // 全局访问点
    public static Singleton getInstance() {
        return instance;
    }
}

2. 懒汉式(Lazy Initialization)

特点 :第一次调用 getInstance() 时才创建实例,节省资源,但非线程安全

java 复制代码
public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        // 非线程安全:多线程下可能创建多个实例
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

3. 双重检查锁(Double-Checked Locking)

特点 :结合了懒汉式的延迟加载和线程安全,是最常用的高效实现方式

java 复制代码
public class Singleton {
    // volatile 禁止指令重排,保证多线程下的可见性
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        // 第一次检查:避免每次都加锁,提升性能
        if (instance == null) {
            // 加锁:保证同一时间只有一个线程进入
            synchronized (Singleton.class) {
                // 第二次检查:防止多个线程同时通过第一次检查后,重复创建实例
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
关键细节
  • volatile 关键字
    • 保证变量的可见性 :一个线程修改了 instance,其他线程能立即看到。
    • 禁止指令重排new Singleton() 分为三步(分配内存、初始化对象、引用赋值),volatile 防止这三步被重排,避免多线程下获取到未初始化的实例。
  • 双重检查
    • 第一次检查:快速判断,避免每次都进入同步块,提升性能。
    • 第二次检查:在同步块内再次判断,确保只创建一个实例。

4. 静态内部类(Static Inner Class)

特点:利用类加载机制实现延迟加载和线程安全,代码简洁,推荐使用。

java 复制代码
public class Singleton {
    private Singleton() {}
    
    // 静态内部类:只有在被调用时才会加载,实现延迟加载
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

5. 枚举(Enum)

特点最简洁、最安全的实现方式,天然防止反射和序列化破坏单例,推荐使用。

java 复制代码
public enum Singleton {
    INSTANCE; // 唯一实例
    
    // 可以添加其他方法
    public void doSomething() {
        // 业务逻辑代码
    }
}

// 使用方式
Singleton.INSTANCE.doSomething();

三、单例模式的破坏与防护

1. 反射破坏

反射可以通过 setAccessible(true) 绕过私有构造,创建多个实例。

防护

  • 在构造方法中判断实例是否已存在,若存在则抛出异常。
java 复制代码
private Singleton() {
    if (instance != null) {
        throw new RuntimeException("单例模式禁止反射创建实例");
    }
}
  • 使用枚举实现,天然防止反射破坏。

2. 序列化破坏

如果单例类实现了 Serializable 接口,反序列化时会创建新的实例。

防护

  • 重写 readResolve() 方法,返回单例实例。
java 复制代码
private Object readResolve() {
    return instance;
}
  • 使用枚举实现,天然防止序列化破坏。

四、实现方式对比

实现方式 线程安全 延迟加载 性能 推荐度
饿汉式 ⭐⭐⭐
懒汉式
双重检查锁 ⭐⭐⭐⭐
静态内部类 ⭐⭐⭐⭐⭐
枚举 ⭐⭐⭐⭐⭐

最佳实践

  • 追求简洁和安全:使用枚举
  • 追求延迟加载和性能:使用静态内部类双重检查锁

五、总结

  1. 单例模式的核心是保证类只有一个实例,并提供全局访问点,构造方法必须私有化。
  2. 饿汉式线程安全但无延迟加载,懒汉式有延迟加载但非线程安全,双重检查锁和静态内部类兼顾了线程安全与延迟加载。
  3. 枚举是最安全的单例实现方式,能天然抵御反射和序列化破坏,推荐优先使用。
相关推荐
云姜.1 天前
单例模式及线程安全问题
单例模式
木井巳2 天前
【多线程】单例模式
java·单例模式·java-ee
忧郁的Mr.Li2 天前
设计模式--单例模式
javascript·单例模式·设计模式
卷卷的小趴菜学编程2 天前
项目篇----仿tcmalloc的内存池设计(page cache)
c++·缓存·单例模式·tcmalloc·内存池·span cache
萧曵 丶3 天前
懒加载单例模式中DCL方式和原理解析
java·开发语言·单例模式·dcl
萧曵 丶3 天前
单例模式 7 种实现方式对比表
java·单例模式
当战神遇到编程7 天前
图书管理系统
java·开发语言·单例模式
Remember_9937 天前
Java 单例模式深度解析:设计原理、实现范式与企业级应用场景
java·开发语言·javascript·单例模式·ecmascript
春日见8 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
短剑重铸之日8 天前
《设计模式》第二篇:单例模式
java·单例模式·设计模式·懒汉式·恶汉式