网站Logo Ilren 小记

Java 设计模式-单例模式

jack
15
2023-05-02

单例模式(Singleton Pattern):让资源类只存在一个实例

✨ 模式简介

单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。
常用于控制全局资源的唯一访问入口,例如配置类、日志器、数据库连接池等。


📦 应用场景

  • 日志系统(Logger)
  • 配置中心(ConfigManager)
  • 缓存(Cache)
  • 数据库连接池(DataSource)
  • 线程池(ThreadPoolExecutor)
  • Spring 默认的 Bean 实例就是单例

🧠 核心实现思路

  1. 构造函数设为 private,防止外部 new;
  2. 提供一个静态私有变量保存实例;
  3. 提供一个公共静态方法访问实例。

🧱 单例模式的多种实现方式

1. 饿汉式(线程安全,推荐)

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

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

  • 优点:简单,线程安全
  • 缺点:类加载即初始化,不管是否使用,可能浪费内存

2. 懒汉式(非线程安全,不推荐)

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 缺点:多线程环境下可能创建多个实例,不安全

3. 懒汉式 + synchronized(线程安全但效率低)

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

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

  • 优点:线程安全
  • 缺点:每次调用都加锁,性能较差

4. 双重检查锁 DCL(推荐)

public class Singleton {
    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 防止指令重排

5. 静态内部类(最推荐)

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

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

  • 优点:线程安全,延迟加载,写法优雅

6. 枚举实现(最安全)

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Hello Singleton!");
    }
}

  • 优点:简洁,线程安全,防止反射和序列化破坏

💣 单例模式的破坏与防御

1.反射攻击

Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newInstance = constructor.newInstance(); // 破坏单例

防御方法

private static boolean initialized = false;

private Singleton() {
    if (initialized) {
        throw new RuntimeException("Instance already created!");
    }
    initialized = true;
}

2.序列化攻击

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj"));
out.writeObject(Singleton.getInstance());

ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj"));
Singleton s = (Singleton) in.readObject(); // 新对象,破坏单例

防御方法

private Object readResolve() {
    return getInstance();
}

📊 优缺点总结

优点 缺点
控制实例数量,节省资源 全局状态影响测试
全局访问点,方便使用 依赖隐藏,耦合度上升
线程安全(合理实现) 容易滥用,设计臃肿

📚 实际应用

  • Spring 容器默认 Bean 是单例作用域
  • 日志系统 Logger
  • 线程池
  • Redis 连接池等

🎯 总结建议

  • 单例适合无状态或读多写少资源类
  • 不滥用单例,依赖注入优先
  • 推荐静态内部类或枚举实现
  • 注意线程安全和防御攻击

一个写代码的人,也写字,记录风吹过生活的声音。

动物装饰