当前位置: 首页 > news >正文

设计模式之【单例模式】

单例模式(Singleton Pattern)

单例模式是设计模式中创建型模式的经典代表,核心思想是:保证一个类在整个应用程序中只有一个实例,并提供一个全局访问点来获取这个实例。
简单来说,单例模式就是让某个类 “独一无二”—— 无论调用多少次创建方法,返回的都是同一个对象,避免重复创建对象消耗资源(比如数据库连接池、配置类、日志工具类等场景)。

单例模式的实际应用场景
工具类:比如 java.lang.Runtime(JDK 内置饿汉单例)、日志工具类、日期工具类;
资源密集型对象:数据库连接池、Redis 连接池、线程池(避免重复创建消耗资源);
配置类:全局配置类(应用启动后加载一次配置,全局复用);
Spring 容器:Spring 中默认所有 Bean 都是单例(通过容器管理唯一实例);
缓存:应用级缓存(比如本地缓存工具,单例保证缓存数据全局一致)。

一、核心设计原则

私有构造方法:禁止外部通过 new 关键字创建实例;
私有静态实例:类内部维护唯一的实例对象;
公共静态方法:提供全局访问点,返回这个唯一实例。

二、Java 中常见的单例实现方式(从易到难,从有坑到最优)

单例模式的实现关键是线程安全和懒加载(按需创建实例),不同实现方式的核心差异也在这两点。

1. 饿汉式(饿汉单例)—— 最简单,线程安全但无懒加载

核心逻辑:类加载时就创建实例(“饿” 意味着迫不及待创建),JVM 类加载机制保证线程安全。

public class HungrySingleton { // 1. 私有静态实例:类加载时直接创建(唯一实例) private static final HungrySingleton INSTANCE = new HungrySingleton(); // 2. 私有构造方法:禁止外部new private HungrySingleton() {} // 3. 公共静态方法:返回唯一实例 public static HungrySingleton getInstance() { return INSTANCE; } // 测试方法 public void doSomething() { System.out.println("饿汉单例执行方法,实例地址:" + this); } }

特点:
✅ 线程安全(JVM 类加载时仅创建一次实例);
❌ 无懒加载(类加载时就创建,即使没用到也会占用内存);
❌ 无法控制实例创建时机(比如需要读取配置后再创建的场景不适用);
✅ 实现简单,无并发问题。

2. 懒汉式(懒汉单例)—— 懒加载但非线程安全(基础版)

核心逻辑:第一次调用 getInstance() 时才创建实例(“懒” 意味着按需创建),但多线程下会创建多个实例。

public class LazySingleton { // 1. 私有静态实例:初始为null(懒加载) private static LazySingleton INSTANCE = null; // 2. 私有构造方法 private LazySingleton() {} // 3. 公共静态方法:首次调用时创建实例 public static LazySingleton getInstance() { if (INSTANCE == null) { // 判空:未创建则new INSTANCE = new LazySingleton(); } return INSTANCE; } public void doSomething() { System.out.println("懒汉单例执行方法,实例地址:" + this); } }

问题:
多线程场景下,多个线程同时进入 if (INSTANCE == null),会创建多个实例(破坏单例特性)。比如:

// 多线程测试 public class SingletonTest { public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { LazySingleton instance = LazySingleton.getInstance(); instance.doSomething(); }).start(); } } }

输出会看到多个不同的实例地址,说明单例被破坏。

3.懒汉式(加锁版)—— 线程安全但性能差

核心逻辑:给 getInstance() 加 synchronized 锁,保证同一时间只有一个线程执行创建逻辑。

public class LazySingletonWithLock { private static LazySingletonWithLock INSTANCE = null; private LazySingletonWithLock() {} // 加synchronized锁,保证线程安全 public static synchronized LazySingletonWithLock getInstance() { if (INSTANCE == null) { INSTANCE = new LazySingletonWithLock(); } return INSTANCE; } }

特点:
✅ 线程安全;
✅ 懒加载;
❌ 性能差(每次调用 getInstance() 都要加锁,即使实例已创建)。

4. 双重检查锁(DCL)—— 线程安全 + 懒加载 + 高性能(推荐)

核心逻辑:两次判空 + 加锁,既保证线程安全,又避免频繁加锁(最优的懒加载单例)。

public class DclSingleton { // 关键:volatile 关键字,禁止指令重排(必须加!) private static volatile DclSingleton INSTANCE = null; private DclSingleton() {} public static DclSingleton getInstance() { // 第一次判空:实例已创建时,直接返回,无需加锁(提升性能) if (INSTANCE == null) { synchronized (DclSingleton.class) { // 类锁,只锁创建逻辑 // 第二次判空:防止多个线程等待锁后重复创建 if (INSTANCE == null) { INSTANCE = new DclSingleton(); } } } return INSTANCE; } }

关键细节:
volatile 必须加:INSTANCE = new DclSingleton() 不是原子操作,分为 3 步:
分配内存;2. 初始化对象;3. 把 INSTANCE 指向内存地址。
不加 volatile 可能发生指令重排(步骤 2 和 3 交换),导致其他线程拿到 “未初始化完成的实例”。
双重判空:第一次判空避免频繁加锁,第二次判空避免多线程重复创建。
特点:
✅ 线程安全;
✅ 懒加载;
✅ 高性能(仅首次创建时加锁);
✅ 实际开发中最常用(比如 Spring 中很多单例 Bean 底层用此方式)。

5. 静态内部类(Holder 模式)—— 线程安全 + 懒加载(优雅版)

核心逻辑:利用 JVM 静态内部类的加载机制,实现懒加载 + 线程安全(无需加锁)。

public class HolderSingleton { // 私有构造方法 private HolderSingleton() {} // 静态内部类:只有调用 getInstance() 时才加载 private static class SingletonHolder { // 内部类加载时创建实例,JVM 保证线程安全 private static final HolderSingleton INSTANCE = new HolderSingleton(); } // 公共方法:返回内部类中的实例 public static HolderSingleton getInstance() { return SingletonHolder.INSTANCE; } }

特点:
✅ 线程安全(JVM 加载内部类时仅创建一次实例);
✅ 懒加载(内部类只有被调用时才加载);
✅ 无锁,性能高;
✅ 代码优雅,推荐使用(比 DCL 更简洁)。

6. 枚举单例 —— 绝对线程安全 + 防反射破坏(终极版)

核心逻辑:利用 Java 枚举的特性,天然保证单例(无法通过反射 / 序列化破坏)。

public enum EnumSingleton { // 唯一实例(枚举常量) INSTANCE; // 业务方法 public void doSomething() { System.out.println("枚举单例执行方法,实例地址:" + this); } } // 直接通过枚举常量获取实例 EnumSingleton instance = EnumSingleton.INSTANCE; instance.doSomething();

特点:
✅ 绝对线程安全(JVM 保证枚举常量唯一);
✅ 防止反射破坏(反射无法创建枚举实例);
✅ 防止序列化破坏(枚举序列化机制特殊,反序列化仍返回同一实例);
❌ 无懒加载(枚举类加载时创建实例);
✅ 《Effective Java》推荐的最优单例方式(无任何坑)。

特点总结

单例模式的核心是保证类只有一个实例 + 全局访问点,核心价值是节省资源、保证状态一致;
实际开发中优先选 双重检查锁(DCL) 或 静态内部类(懒加载 + 高性能),追求绝对安全选 枚举单例;
避免使用基础版懒汉式(线程不安全)和加锁懒汉式(性能差);
单例模式的坑主要在线程安全和指令重排,DCL 中 volatile 必须加,枚举单例可规避所有坑。

http://www.cnnetsun.cn/news/1859640.html

相关文章:

  • 卷王问卷考试系统 JMeter 压测报告分析
  • 终极Steam创意工坊下载指南:WorkshopDL免费跨平台解决方案
  • WarcraftHelper终极指南:让魔兽争霸3在现代系统重获新生
  • 无锁队列简介
  • Napkin AI:用文字一键生成专业图表的神器
  • 如何快速实现WPS与Zotero无缝集成:终极学术写作效率指南
  • 【无标题】学习codewrrior
  • GEO 怎么优化?2026 年AI搜索优化完整指南与 5 家权威 GEO 优化服务商选型指南
  • 山东大学创新实训-智能体驱动的沉浸式剧本杀互动系统(一)
  • Windows Defender Remover架构解析:深度剖析系统安全组件移除的实现原理
  • MySQL Explain 输出结果与执行逻辑分析
  • XML Notepad:告别XML编辑噩梦的终极解决方案
  • 计算机网络深度解析:数据链路层传输的数据单元究竟是什么?——从帧结构到协议实现的万字全解
  • Qwen3-Embedding-4B镜像部署教程:NVIDIA驱动/CUDA/cuDNN版本兼容性避坑指南
  • RexUniNLU实战案例:气象预报文本中时间/地点/天气现象/强度等级四元组抽取
  • Zotero PDF预览插件终极指南:告别频繁切换,实现高效文献管理
  • 辅助驾驶场景应用:如何用视觉定位模型理解道路目标
  • Expose部署实战:免费托管摄影作品集的3种最佳方案
  • ncmdumpGUI:网易云NCM文件格式转换的C实现方案深度解析
  • Amazon DSSTNE高级配置技巧:激活函数、权重初始化与优化器选择终极指南
  • RTX 4090D+PyTorch 2.8实战:从零开始你的第一个AI项目
  • amphp/amp 完全指南:如何快速掌握 PHP 异步编程新范式
  • 京东完成100亿元票据发行 部分用于偿还现有债务及支付利息
  • SAP ABAP | WBS 元素 24 位(外部)与 8 位(内部)编码转换的分析
  • 如何构建个人游戏串流服务器:Sunshine跨平台低延迟完整方案
  • 深入理解 js-base64:从 TypeScript 到 ES5 的完整编译流程解析
  • 从源码到生产:lz-string压缩库的完整部署与发布指南
  • 如何成为算法工程师:从GitHub_Trending/pyt/Python项目开始的成长路径
  • CHORD-X模型微调实战:使用特定领域数据优化金融报告生成
  • 猫抓扩展终极指南:3步掌握浏览器视频资源嗅探技巧