侧边栏壁纸
  • 累计撰写 29 篇文章
  • 累计创建 38 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Java 单例模式的8种经典实现

16uni
2025-06-13 / 0 评论 / 0 点赞 / 28 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

以下是 Java 单例模式的 8 种经典实现,结合线程安全性、延迟加载、防反射等维度深度解析,并附应用场景对比表。以咖啡店店长为例通俗化讲解:


🔒 一、饿汉式(Eager Initialization)

public class CafeManager {
    private static final CafeManager INSTANCE = new CafeManager(); // 开业前就选好店长
    private CafeManager() {} // 禁止外部招聘新店长
    public static CafeManager getInstance() {
        return INSTANCE; // 直接返回唯一店长
    }
}
  • 原理:类加载时立即初始化(类似咖啡店装修时已确定店长)

  • 优点:线程安全(JVM 类加载机制保证)、实现简单

  • 缺点:可能浪费资源(店长闲置时也占着位置)


😴 二、懒汉式(线程不安全版)

public class CafeManager {
    private static CafeManager instance;
    private CafeManager() {}
    public static CafeManager getInstance() {
        if (instance == null) { // 临时招店长
            instance = new CafeManager(); 
        }
        return instance;
    }
}
  • 原理:首次调用时创建实例(顾客上门才招店长)

  • 缺点:多线程下可能创建多个店长(多个顾客同时下单招人)❌ 禁止生产使用


🔐 三、懒汉式(同步方法版)

public class CafeManager {
    private static CafeManager instance;
    private CafeManager() {}
    public static synchronized CafeManager getInstance() {
        if (instance == null) {
            instance = new CafeManager(); 
        }
        return instance;
    }
}
  • 改进synchronized 锁方法保证线程安全(只允许一个顾客招聘店长)

  • 缺点:每次调用都加锁,性能差(每个顾客都要排队问店长在不在)


⚡ 四、双重检查锁(DCL 终极版)

public class CafeManager {
    private static volatile CafeManager instance; // volatile 防指令重排
    private CafeManager() {}
    public static CafeManager getInstance() {
        if (instance == null) { // 第一次检查:避免不必要的锁竞争
            synchronized (CafeManager.class) {
                if (instance == null) { // 第二次检查:确保唯一性
                    instance = new CafeManager();
                }
            }
        }
        return instance;
    }
}
  • 原理:两次判空 + volatile 保证可见性与有序性(店长招聘室双重门禁)

  • 优点:线程安全 + 延迟加载 + 高性能(多数情况无需加锁)

  • 适用:高并发场景(网红咖啡店高峰期)


📦 五、静态内部类(推荐)

public class CafeManager {
    private CafeManager() {}
    private static class ManagerHolder {
        static final CafeManager INSTANCE = new CafeManager(); // 内部类持店长
    }
    public static CafeManager getInstance() {
        return ManagerHolder.INSTANCE; // 首次调用时加载内部类
    }
}
  • 原理:利用类加载机制(当访问 ManagerHolder 时才初始化店长)

  • 优点:线程安全(JVM 保证类加载同步)、天然延迟加载、无锁高性能

  • Java 内存模型保证静态内部类加载仅一次


🏷️ 六、枚举式(最安全)

public enum CafeManager {
    INSTANCE; // 枚举常量即单例
    public void openCafe() { 
        System.out.println("咖啡店营业!");
    }
}
// 使用:CafeManager.INSTANCE.openCafe();
  • 原理:枚举类型天生单例(JVM 层保障)

  • 优势

    • 绝对线程安全

    • 自动防反射攻击(无法通过反射创建枚举)

    • 自动处理序列化(反序列化不生成新对象)

  • Effective Java 作者推荐


🧵 七、ThreadLocal 式(线程级单例)

public class ThreadLocalManager {
    private static final ThreadLocal<ThreadLocalManager> instance =
        ThreadLocal.withInitial(ThreadLocalManager::new); // 每个线程独立店长
    public static ThreadLocalManager getInstance() {
        return instance.get();
    }
}
  • 原理:每个线程拥有独立实例(分店各自有店长)

  • 适用场景

    • 线程上下文隔离(如数据库连接管理)

    • 避免同步但非全局单例


⚛️ 八、CAS 原子操作式(无锁实现)

public class CASManager {
    private static final AtomicReference<CASManager> INSTANCE = 
        new AtomicReference<>();
    private CASManager() {}
    public static CASManager getInstance() {
        for (;;) {
            CASManager instance = INSTANCE.get();
            if (instance != null) return instance;
            instance = new CASManager();
            if (INSTANCE.compareAndSet(null, instance)) // CAS 原子操作
                return instance;
        }
    }
}
  • 原理:CAS(Compare-And-Swap)无锁更新(店长竞选投票机制)

  • 优点:避免线程阻塞,高并发性能优

  • 缺点:可能多次实例化(但仅存一个有效),实现复杂


🛡️ 单例模式防破坏技巧

攻击方式

解决方案

支持场景

反射攻击

枚举式 ✅ / 构造器抛异常 ❌

枚举、饿汉式

序列化破坏

重写 readResolve() 方法

非枚举类

克隆破坏

重写 clone() 抛异常

所有实现


📊 单例模式应用场景与选择指南

场景

推荐实现

理由

简单应用,无性能要求

饿汉式

实现简单,线程安全

高并发延迟加载

静态内部类

无锁安全 + 按需加载

需防反射/序列化攻击

枚举式

JVM 原生支持安全防护

分布式环境

结合分布式锁

防止多 JVM 实例问题

线程隔离数据

ThreadLocal

各线程独立实例

💡 黄金口诀 “一私一静一全局” —— 私有构造、静态实例、全局访问点 延迟加载选静态内部类,绝对安全用枚举!

通过这 8 种实现 + 防破坏技巧 + 场景对照表,可彻底掌握单例模式精髓。实际开发中优先选择 静态内部类(延迟加载)或 枚举(安全防护),避免过度设计。

0

评论区