单例模式 超详细完整版

一、单例模式是什么?

单例模式(Singleton)创建型设计模式核心定义保证一个类在整个程序运行中,有且仅有一个实例对象,并提供一个全局访问入口。

二、单例模式三大核心特点(必背)

  1. 私有构造方法:禁止外部 new 对象
  2. 私有静态实例:类内部唯一对象
  3. 公共静态方法:全局唯一获取实例入口

三、单例解决什么问题?

  1. 避免重复创建对象,节省内存
  2. 全局统一资源管控(连接池、配置类、工具类)
  3. 保证操作唯一性(日志、计数器、线程池)

四、两大分类

  1. 饿汉式 :类加载时直接创建实例 → 无懒加载、线程安全
  2. 懒汉式 :使用时才创建实例 → 懒加载、省内存、需要解决线程安全

五、八种实现方式

1. 饿汉式(静态常量)【可用、简单】

特点 :类加载直接初始化,天生线程安全

java

复制代码
public class Singleton {
    // 1. 静态常量实例
    private static final Singleton instance = new Singleton();

    // 2. 私有构造
    private Singleton(){}

    // 3. 全局获取
    public static Singleton getInstance(){
        return instance;
    }
}

✅ 优点:简单、无锁、线程绝对安全、性能高 ❌ 缺点:无懒加载,类加载就创建,不用也占内存


2. 饿汉式(静态代码块)

适合:需要初始化额外资源

java

复制代码
public class Singleton {
    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

3. 懒汉式(线程不安全)

特点:用时才创建,多线程会创建多个实例

java

复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

❌ 致命问题:多线程并发会创建多个对象


4. 懒汉式(同步方法)

java

复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    // 加锁
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

✅ 线程安全 ❌ 每次获取都加锁,高并发性能极差


5. 懒汉式(同步代码块)

java

复制代码
// 错误!不能保证单例
public static Singleton getInstance(){
    if(instance == null){
        synchronized (Singleton.class){
            instance = new Singleton();
        }
    }
    return instance;
}

双重检查缺失第一层锁判断,多线程依旧会重复创建


6. 双重检查锁 DCL

终极优化:懒加载 + 线程安全 + 高性能

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?(面试必考)

new 对象 底层三步:

  1. 开辟内存空间
  2. 初始化对象
  3. 赋值给 instance

CPU 指令重排 可能变成:1→3→2 导致其他线程拿到 半初始化对象,程序报错。

volatile 作用:禁止指令重排、保证可见性

✅ 优点:懒加载、线程安全、并发高性能 ❌ 写法稍复杂、需要理解 volatile


7. 静态内部类单例

java

复制代码
public class Singleton {
    // 私有构造
    private Singleton(){}

    // 静态内部类:外部类加载不会加载内部类
    private static class Inner{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return Inner.INSTANCE;
    }
}
原理(满分解释)
  1. 外部类加载不加载内部类 → 实现懒加载
  2. 内部类静态变量初始化由 JVM 保证线程安全
  3. 调用 getInstance() 才加载内部类,创建唯一实例

✅ 懒加载 ✅ 无锁、性能极高 ✅ JVM 天然线程安全 🔥 面试最推荐、代码简洁、无坑

❌ 缺点:无法防止反射破坏单例


8. 枚举单例

java

复制代码
public enum Singleton {
    INSTANCE;

    // 自定义方法
    public void doSomething(){
        System.out.println("业务方法");
    }
}

使用:

java

复制代码
Singleton.INSTANCE.doSomething();
最强优点
  1. 绝对线程安全
  2. 杜绝反射破坏、序列化破坏
  3. 写法极简
  4. 天然单例、不会多创建

Java 官方公认最完美单例 ❌ 不支持懒加载(类加载直接初始化)


六、四大破坏单例的方式 & 防护

1. 反射破坏

通过暴力反射创建新对象,除了枚举 全部可破。 枚举天然防反射

2. 序列化破坏

对象序列化再反序列化会生成新对象。 枚举天然防序列化。

3. 克隆破坏

重写 clone 方法会产生新对象。

4. 多线程并发

普通懒汉式无锁直接失效。


七、八种单例终极对比表(背诵版)

表格

方式 懒加载 线程安全 性能 防反射 推荐度
饿汉静态常量 极高 ⭐⭐⭐
饿汉静态代码块 极高 ⭐⭐⭐
普通懒汉 ❌废弃
同步方法懒汉 极低 ⭐⭐
同步代码块懒汉 ❌错误
DCL 双重检查 极高 ⭐⭐⭐⭐
静态内部类 极高 ⭐⭐⭐⭐⭐
枚举 极高 ⭐⭐⭐⭐⭐⭐

八、开发中到底用哪个?

  1. 需要懒加载、高性能、无漏洞 → 静态内部类
  2. 高并发、框架底层、绝对安全 → 枚举
  3. 简单工具类、无需懒加载 → 饿汉式
  4. 面试手写最优代码 → DCL / 静态内部类

九、单例模式真实业务场景

  1. Spring Bean 默认单例
  2. 数据库连接池
  3. Redis 连接工具类
  4. 全局配置类、系统参数
  5. 日志工具、计数器
  6. 线程池对象
  7. 系统缓存管理器

十、面试高频问答

1. DCL 为什么要 volatile?

防止 JVM 指令重排,避免线程获取半初始化对象,保证多线程安全。

2. 静态内部类原理?

利用外部类不加载内部类 实现懒加载,利用JVM 静态初始化线程安全保证单例。

3. 枚举为什么是最完美单例?

JVM 底层禁止反射枚举构造方法,天然杜绝反射、序列化、多线程问题。

4. 饿汉和懒汉区别?

饿汉:类加载创建,无懒加载、省锁、费内存 懒汉:用时创建,懒加载、省内存、需要控线程安全