教程中心 / Java教程 / 单例模式

单例模式

10分钟 设计模式

什么是单例模式?

单例模式(Singleton Pattern)是设计模式中最简单但最常用的一种。它的核心思想是:确保一个类只有一个实例,并提供一个全局访问点

生活比喻

一个国家只能有一个总统(否则就乱套了)。这个总统的职位就是"单例"——全局唯一,只能有一个人担任。任何人想成为总统,都必须去找"总统"这个职位本身,而不是自己创建一个新的总统职位。

单例模式的核心特征:

1. 私有构造方法 - 外部不能 new 这个类
   private Singleton() {}

2. 私有静态实例 - 类内部自己创建唯一实例
   private static final Singleton INSTANCE = ...;

3. 公开静态方法 - 外部获取唯一实例的入口
   public static Singleton getInstance() { return INSTANCE; }

为什么需要单例模式?

共享资源

全局共享的数据,如数据库连接池、配置管理器

减少开销

避免重复创建和销毁对象,提升性能

全局协调

统一管理共享资源,如日志记录器

四种实现方式详解

1. 饿汉式(推荐指数:★★★★☆)

类加载时就创建实例,线程安全,但可能造成资源浪费(如果从未使用)

public class HungrySingleton {
    // 类加载时就创建实例
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    // 私有构造方法
    private HungrySingleton() {
        System.out.println("创建 HungrySingleton 实例");
    }

    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

// 特点:
// ✓ 线程安全(类加载时就由JVM保证)
// ✓ 调用效率高(直接返回实例)
// ✗ 延迟加载?不支持(类加载时就创建)

2. 懒汉式(推荐指数:★★☆☆☆)

首次调用时才创建实例,支持延迟加载,但线程不安全(需要加synchronized)

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
        System.out.println("创建 LazySingleton 实例");
    }

    // synchronized 保证线程安全,但性能开销大
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

// 特点:
// ✓ 延迟加载(需要时才创建)
// ✓ 线程安全(synchronized)
// ✗ 性能问题:每次调用都要获取锁

3. 双重检查锁(推荐指数:★★★★★)

既支持延迟加载,又是线程安全的,是最常用的单例实现方式

public class DoubleCheckSingleton {
    // volatile 防止指令重排序
    private static volatile DoubleCheckSingleton instance;

    private DoubleCheckSingleton() {
        System.out.println("创建 DoubleCheckSingleton 实例");
    }

    public static DoubleCheckSingleton getInstance() {
        // 第一次检查:避免不必要的同步
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                // 第二次检查:防止多线程重复创建
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

// 特点:
// ✓ 延迟加载
// ✓ 线程安全
// ✓ 高性能(避免不必要的同步)
// 注意:volatile 关键字防止指令重排序

4. 静态内部类(推荐指数:★★★★★)

利用Java类加载机制实现延迟加载和线程安全,是最简洁的实现方式

public class StaticInnerSingleton {
    private StaticInnerSingleton() {
        System.out.println("创建 StaticInnerSingleton 实例");
    }

    // 静态内部类,JVM保证线程安全
    private static class Holder {
        private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
    }

    public static StaticInnerSingleton getInstance() {
        return Holder.INSTANCE;
    }
}

// 特点:
// ✓ 延迟加载(Holder类不会在Main类加载时加载)
// ✓ 线程安全(JVM类加载机制保证)
// ✓ 高性能(无需同步)
// ✓ 简洁:这是作者最推荐的方式!

四种实现方式对比

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

单例模式的破坏和防护

警告:单例模式可能被破坏!

// 1. 反射攻击
// 通过反射可以调用私有构造方法创建新实例
Constructor constructor = DoubleCheckSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
DoubleCheckSingleton s1 = constructor.newInstance();

// 防护:在构造方法中检查实例是否已存在
private DoubleCheckSingleton() {
    if (instance != null) {
        throw new RuntimeException("单例模式被破坏!");
    }
}

// 2. 序列化攻击
// 通过反序列化可以创建新实例
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.db"));
oos.writeObject(DoubleCheckSingleton.getInstance());

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.db"));
DoubleCheckSingleton s2 = (DoubleCheckSingleton) ois.readObject();

// 防护:添加 readResolve 方法返回唯一实例
private Object readResolve() {
    return getInstance();
}

单例模式的应用场景

1

数据库连接池

整个应用共享一个连接池,避免频繁创建销毁连接

2

配置管理器

全局配置信息,如数据库配置、服务器配置

3

日志记录器

如Log4j、SLF4J的Logger通常也是单例

4

任务调度器

系统中只能有一个任务调度中心

小结

  • 单例模式确保一个类只有一个实例,并提供全局访问点
  • 饿汉式:类加载时创建,线程安全,但不支持延迟加载
  • 懒汉式:延迟加载,但synchronized导致性能低
  • 双重检查锁:既延迟加载又线程安全,推荐使用
  • 静态内部类:利用JVM类加载机制,推荐使用
  • 注意防护反射序列化对单例的破坏
☕ Java 在线代码编辑器
📝 运行结果