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

设计模式实战解读(一):单例模式——全局唯一实例的正确打开方式

本文是「设计模式实战解读」系列第一篇。系列文章统一按照定义 → 痛点场景 → 模式结构 → 核心实现 → 真实应用 → 常见变种 → 优缺点 → 避坑指南 → FAQ的结构展开每篇聚焦一个模式讲透。一句话定义单例模式Singleton确保一个类只有一个实例并提供一个全局访问点。归属创建型模式。一、没有单例时的痛点假设你正在做一个配置管理模块系统启动时需要从 Nacos/Apollo 加载配置并缓存到内存里// 问题代码每次需要配置时都 new 一个ConfigManagerconfigAnewConfigManager();// 加载一次远程配置ConfigManagerconfigBnewConfigManager();// 又加载一次远程配置// configA 和 configB 是两个独立实例// 1. 重复加载浪费网络 IO// 2. 两份缓存不一致A 修改了配置B 看不到// 3. 如果配置里有状态如 version两份实例会分裂类似的痛点还出现在数据库连接池、线程池管理器、日志打印器、ID 生成器——这些组件如果被 new 多份要么浪费资源要么产生不一致的行为。核心诉求全局只需要一份任何地方拿到的都是同一个。二、模式结构┌──────────────────────────────┐ │ Singleton │ ├──────────────────────────────┤ │ - instance: Singleton │ ← 唯一实例静态字段 ├──────────────────────────────┤ │ - Singleton() │ ← 私有构造禁止外部 new │ getInstance(): Singleton │ ← 全局访问点 │ businessMethod() │ ← 业务方法 └──────────────────────────────┘三要素私有构造函数——禁止外部new静态实例字段——类级别持有唯一实例公开静态方法——全局获取入口三、核心实现五种写法对比3.1 饿汉式推荐在大多数场景使用publicclassSingleton{// 类加载时就创建实例JVM 保证线程安全privatestaticfinalSingletonINSTANCEnewSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returnINSTANCE;}}优点实现简单线程安全无同步开销。缺点类加载时就创建如果实例很重且未必被使用造成浪费。适用实例创建成本低、确定会被使用的场景。3.2 懒汉式 双重检查锁DCLpublicclassSingleton{// volatile 防止指令重排序privatestaticvolatileSingletoninstance;privateSingleton(){}publicstaticSingletongetInstance(){if(instancenull){// 第一次检查无锁synchronized(Singleton.class){// 加锁if(instancenull){// 第二次检查instancenewSingleton();}}}returninstance;}}优点懒加载 线程安全 锁粒度小。缺点代码稍复杂volatile 有轻微性能开销。适用实例创建成本高、不确定是否会被使用。为什么需要 volatile因为instance new Singleton()不是原子操作JVM 可能先分配内存、再赋值引用、最后执行构造函数指令重排。不加 volatile其他线程可能拿到一个半初始化的实例。3.3 静态内部类推荐的懒加载方案publicclassSingleton{privateSingleton(){}// 内部类在第一次被引用时才加载JVM 保证线程安全privatestaticclassHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}publicstaticSingletongetInstance(){returnHolder.INSTANCE;}}优点懒加载 线程安全 无同步开销 代码简洁。缺点无法传参初始化。适用大多数需要懒加载的场景。这是实际项目中最推荐的写法。3.4 枚举单例最安全的写法publicenumSingleton{INSTANCE;privatefinalAtomicLongcounternewAtomicLong(0);publiclongnextId(){returncounter.incrementAndGet();}}// 使用longidSingleton.INSTANCE.nextId();优点天然防反射、防序列化破坏、代码极简、线程安全。缺点不能继承其他类枚举隐式 extends Enum、无法懒加载。适用对安全性要求极高、防止反射攻击的场景。Effective Java 推荐的写法。3.5 五种写法对比写法线程安全懒加载防反射防序列化代码复杂度饿汉式✓✗✗✗低DCL✓✓✗✗中静态内部类✓✓✗✗低枚举✓✗✓✓最低容器管理 (Spring)✓✓N/AN/A零框架做四、真实应用场景4.1 框架级应用Spring IoC 容器Spring Bean 默认scopesingleton。整个容器中同一个 BeanDefinition 只有一个实例。这不是 GoF 单例不是类级别唯一而是容器级别唯一——但核心思想一致。Runtime.getRuntime()JDK 标准库中的经典饿汉单例。Slf4j LoggerFactory每个类获取的 Logger 实例在内部是缓存的同一个 name 返回同一个实例。4.2 业务级应用业务场景单例对象为什么用单例数据库连接池HikariDataSource多份连接池浪费连接资源分布式 ID 生成Snowflake Worker全局唯一 workerId 保证不重复配置中心客户端NacosConfigManager只需一份缓存变更统一监听本地缓存Caffeine Cache缓存命中率依赖数据集中在一处限流器RateLimiter全局统一计数才能准确限流线程池ThreadPoolExecutor多份线程池破坏全局资源控制4.3 iPaaS 场景中的典型单例在流程引擎类项目中以下组件适合用单例FlowOrchestrator流程编排器编排逻辑无状态全局一个实例即可InterruptSignalCache中断信号缓存全局 Guava Cache所有流程共享ExecutionMetrics执行指标收集全局计数器汇总后推送到监控系统SnowflakeIdGeneratorID 生成器基于 workerId 的全局唯一实例五、常见变种5.1 多例模式Multiton有时不是全局只要一个而是某个 key 对应一个。比如按租户 ID 隔离的缓存实例publicclassTenantCache{privatestaticfinalMapString,TenantCacheINSTANCESnewConcurrentHashMap();privateTenantCache(StringtenantId){// 初始化该租户的缓存}publicstaticTenantCachegetInstance(StringtenantId){returnINSTANCES.computeIfAbsent(tenantId,TenantCache::new);}}5.2 可销毁单例某些场景下热加载、测试隔离需要销毁后重建单例publicclassReloadableSingleton{privatestaticvolatileReloadableSingletoninstance;publicstaticvoiddestroy(){instancenull;// 销毁}publicstaticReloadableSingletongetInstance(){if(instancenull){synchronized(ReloadableSingleton.class){if(instancenull){instancenewReloadableSingleton();}}}returninstance;}}5.3 线程级单例ThreadLocal全局唯一不是诉求线程内唯一才是publicclassThreadLocalSingleton{privatestaticfinalThreadLocalThreadLocalSingletonINSTANCEThreadLocal.withInitial(ThreadLocalSingleton::new);publicstaticThreadLocalSingletongetInstance(){returnINSTANCE.get();}}典型场景JDBC Connection线程内复用线程间隔离、RequestContext。六、优缺点优点缺点全局唯一避免重复创建隐藏了类之间的依赖关系共享资源的统一管控对单元测试不友好全局状态难 mock延迟初始化节省资源违反单一职责既管创建又管业务提供全局访问点多线程场景容易踩坑七、避坑指南坑 1反射攻击破坏单例// 恶意代码通过反射绕过私有构造ConstructorSingletoncSingleton.class.getDeclaredConstructor();c.setAccessible(true);Singletonanotherc.newInstance();// 第二个实例防御在构造函数里加校验privateSingleton(){if(INSTANCE!null){thrownewIllegalStateException(Singleton already initialized);}}或者直接用枚举单例JVM 禁止反射创建枚举实例。坑 2序列化/反序列化破坏单例实现了 Serializable 的单例反序列化时会创建新实例。防御添加readResolve()方法privateObjectreadResolve(){returnINSTANCE;// 反序列化时返回已有实例}坑 3Spring 中误用 prototype scopeSpring Bean 默认是 singleton但如果一个 singleton Bean 注入了一个 prototype Beanprototype 不会每次都新建——因为注入只发生一次。防御用Lookup注解或ObjectFactoryT来获取 prototype Bean。坑 4单例持有可变状态导致线程安全问题单例本身是安全的但如果它持有可变状态如 HashMap多线程并发读写会出问题。防御单例的内部字段要么不可变final要么用线程安全容器ConcurrentHashMap、AtomicLong。坑 5类加载器隔离导致多个单例在 Tomcat 等容器中不同 ClassLoader 会各自加载一份类——导致看似是单例实际有多个实例。防御确保单例类在 parent ClassLoader 中加载或者用容器提供的单例管理机制。八、常见问题FAQQSpring 的 Bean 是单例模式吗ASpring 的 singleton scope 是容器级别的唯一每个 ApplicationContext 维护一份不是 GoF 意义上的类级别唯一。一个类在多个 ApplicationContext 中可以有多个实例。但在业务代码中效果等同于单例因为通常只有一个容器。Q单例和静态类工具类有什么区别A静态类不能实现接口、不能被 mock、不能被 Spring 管理、不能做延迟初始化。单例是一个对象可以实现接口、可以被注入、可以多态。如果组件需要面向接口编程或被测试框架 mock用单例如果纯粹是无状态的工具方法用静态类。Q微服务时代还需要单例吗A需要。微服务让进程级别的全局范围变小了从整个系统缩小到单个服务内但单个服务内依然有全局唯一的诉求——连接池、缓存、配置客户端、ID 生成器。单例的适用范围从不跨进程边界。Q什么情况下不应该用单例A① 对象持有大量请求级别的状态应该每次 new② 对象需要在测试中被频繁替换应该用依赖注入③ 对象的生命周期比进程短如用户会话级对象。QDCL 中 volatile 能不能省略A不能。省略 volatile 会导致指令重排序问题——线程 A 可能观察到 instance 非 null但实例还未完成构造函数的初始化。这在高并发下是真实的 bugJDK 5 的 volatile 语义才修复了这个问题。九、小结单例模式是最简单的设计模式也是最容易用错的。核心记住三点优先用静态内部类或枚举不要写 DCL 除非有充分理由Spring 项目里直接用 Component Autowired让框架管单例单例内部状态必须线程安全——这是 90% 单例 bug 的来源下一篇我们聊工厂模式——当对象创建变得复杂时如何把创建逻辑从业务代码中解耦出来。标签#设计模式 #单例模式 #Singleton #Java #Spring #线程安全 #DCL #volatile #枚举单例 #创建型模式 #软件工程 #面向对象

相关文章:

设计模式实战解读(一):单例模式——全局唯一实例的正确打开方式

本文是「设计模式实战解读」系列第一篇。系列文章统一按照 定义 → 痛点场景 → 模式结构 → 核心实现 → 真实应用 → 常见变种 → 优缺点 → 避坑指南 → FAQ 的结构展开,每篇聚焦一个模式讲透。一句话定义 单例模式(Singleton)&#xff1a…...

Linuxptp从入门到排查:一份覆盖安装、配置与常见报错解决的保姆级指南

Linuxptp从入门到排查:一份覆盖安装、配置与常见报错解决的保姆级指南当你在数据中心里部署高精度时间同步服务时,突然发现日志里不断跳出master offset超限警告;或者当你按照教程配置完ptp4l后,时钟状态始终卡在s0无法锁定——这…...

保姆级教程:一步步教你排查并修复Windows上原神启动器的Qt平台插件错误

彻底解决Windows原神启动器Qt插件报错:零基础环境变量修改指南当你满心欢喜地双击原神启动器图标,准备开启提瓦特大陆的冒险时,屏幕上突然弹出的错误提示框瞬间浇灭了热情:"This application failed to start because no Qt …...

5个简单技巧让明日方舟桌宠Ark-Pets运行更流畅:性能优化完全指南

5个简单技巧让明日方舟桌宠Ark-Pets运行更流畅:性能优化完全指南 【免费下载链接】Ark-Pets Arknights Desktop Pets | 明日方舟桌宠 (ArkPets) 项目地址: https://gitcode.com/gh_mirrors/ar/Ark-Pets Ark-Pets是一款让《明日方舟》角色在桌面上活动的开源桌…...

用Python+Mediapipe+OpenCV做个手势识别小游戏(附完整源码和避坑指南)

用PythonMediapipeOpenCV打造手势控制太空射击游戏 最近在整理旧项目时,翻出一个用Mediapipe手势识别控制的小游戏原型。这个太空射击游戏完全通过手势操作——食指瞄准,握拳射击,手掌移动控制飞船位置。当时为了调试手势映射逻辑&#xff0c…...

3分钟快速解密Navicat密码:开源工具终极指南

3分钟快速解密Navicat密码:开源工具终极指南 【免费下载链接】navicat_password_decrypt 忘记navicat密码时,此工具可以帮您查看密码 项目地址: https://gitcode.com/gh_mirrors/na/navicat_password_decrypt 当您忘记Navicat中保存的数据库连接密码时&#…...

硕士论文写作的技巧有哪些?

先说一句过来人的大实话:硕士论文拼的不是“会不会写”,而是“会不会少走弯路”。因为很多人不是不会写。是:方向选错了框架搭歪了方法乱用了导师意见没听懂写到最后推倒重来这才最伤。真有用的技巧,我讲点实战的。1. 选题别贪大&…...

深度解析Reloaded-II架构:高级模组依赖管理与循环依赖解决方案

深度解析Reloaded-II架构:高级模组依赖管理与循环依赖解决方案 【免费下载链接】Reloaded-II Universal .NET Core Powered Modding Framework for any Native Game X86, X64. 项目地址: https://gitcode.com/gh_mirrors/re/Reloaded-II Reloaded-II作为一款…...

记忆学习导向的高速运动感知图像的去模糊及目标识别【附数据】

✨ 长期致力于深度卷积网络、长短期记忆网络、相机高速运动感知、运动去模糊、运动目标识别研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)融合DCNN与…...

DLSS Swapper完全指南:智能管理游戏DLSS版本的开源革命

DLSS Swapper完全指南:智能管理游戏DLSS版本的开源革命 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 你是否曾在《赛博朋克2077》中为DLSS版本过旧导致的画面闪烁而烦恼?是否因为《控制》中的…...

想知道你的AI提示词到底用了多少Token?这个可视化工具告诉你答案

想知道你的AI提示词到底用了多少Token?这个可视化工具告诉你答案 【免费下载链接】tiktokenizer Online playground for OpenAPI tokenizers 项目地址: https://gitcode.com/gh_mirrors/ti/tiktokenizer 在构建AI应用时,你是否经常遇到这样的困惑…...

Linux系统服务“窃听”与“喊话”:dbus-monitor/dbus-send实战指南(以systemd-logind为例)

Linux系统服务的“窃听”与“喊话”:dbus-monitor/dbus-send高阶实战指南当你坐在咖啡馆里,周围此起彼伏的对话声中,偶尔会捕捉到一些有趣的片段——这正是dbus-monitor在Linux系统中的角色。而当你需要主动与某人交流时,清晰明确…...

3分钟掌握Translumo:免费实时屏幕翻译工具终极指南

3分钟掌握Translumo:免费实时屏幕翻译工具终极指南 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Translumo 你是否曾经…...

如何快速实现蓝奏云直链解析:5分钟搭建专业API服务

如何快速实现蓝奏云直链解析:5分钟搭建专业API服务 【免费下载链接】LanzouAPI 蓝奏云直链,蓝奏api,蓝奏解析,蓝奏云解析API,蓝奏云带密码解析 项目地址: https://gitcode.com/gh_mirrors/la/LanzouAPI 你是否曾…...

告别虚拟机卡顿:在VMware里给CentOS 7最小化安装分配多少内存和CPU才够用?

虚拟机性能优化指南:CentOS 7最小化安装的资源分配策略在个人电脑上运行虚拟机时,最令人头疼的问题莫过于性能瓶颈。许多初学者在创建CentOS 7虚拟机时,常常陷入两难境地:分配过多资源会影响宿主机运行,分配过少又会导…...

再论观点“C++是否应避免使用普通指针,而使用智能指针(包括shared,unique,weak)”

再论观点“C是否应避免使用普通指针,而使用智能指针(包括shared,unique,weak)” PS:笔者这次投稿的问题是:https://www.zhihu.com/question/319277442。老规矩,顺手投稿的问题&…...

KMS_VL_ALL_AIO:开源智能激活工具让Windows和Office激活变得简单

KMS_VL_ALL_AIO:开源智能激活工具让Windows和Office激活变得简单 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统未激活的水印烦恼吗?Office软件频繁弹…...

告别抢票焦虑:DamaiHelper大麦网自动化抢票工具完整指南

告别抢票焦虑:DamaiHelper大麦网自动化抢票工具完整指南 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 你是否经历过这样的时刻?心心念念的演唱会门票开售,你…...

机器学习项目开发模式解析:从提交历史看规模、协作与演化规律

1. 项目概述:从代码提交中解码机器学习项目的真实工作流在机器学习项目的日常开发中,我们每天都在与Git打交道,提交代码、更新模型、调整参数。但你是否想过,这些看似随意的提交背后,是否隐藏着某种规律?一…...

magic - trace:高分辨率追踪利器,解决应用难题,还能深入洞悉程序运行!

magic - trace 概述magic - trace 能够收集并展示进程活动的高分辨率追踪信息,可用于解决生产环境中应用程序处理请求速度慢、了解代码实际运行情况、获取应用崩溃前活动历史等问题。它性能开销在 2% - 10% 之间,使用时无需修改应用程序,能以…...

Windows 11/10下,Microsoft Store打不开?手把手教你用CheckNetIsolation命令批量解除UWP网络隔离

Windows 11/10下Microsoft Store无法连接的终极解决方案:CheckNetIsolation命令详解当你在Windows 11或10上突然发现Microsoft Store无法加载内容,或者Edge浏览器部分网页打不开,而其他网络连接却正常时,这很可能是因为UWP应用的网…...

告别图片搜索焦虑:如何在本地硬盘中秒级找到任何相似图片

告别图片搜索焦虑:如何在本地硬盘中秒级找到任何相似图片 【免费下载链接】ImageSearch 基于.NET10的本地硬盘千万级图库以图搜图案例Demo和图片exif信息移除小工具分享 项目地址: https://gitcode.com/gh_mirrors/im/ImageSearch 还在为硬盘里成千上万的图片…...

Applite:3步告别命令行,让Mac应用管理变得如此简单

Applite:3步告别命令行,让Mac应用管理变得如此简单 【免费下载链接】Applite User-friendly GUI macOS application for Homebrew Casks 项目地址: https://gitcode.com/gh_mirrors/ap/Applite 你是否曾因安装一个应用而被迫打开终端,…...

免费快速搞定CTF MISC难题:5个PuzzleSolver实战技巧让你秒变大神

免费快速搞定CTF MISC难题:5个PuzzleSolver实战技巧让你秒变大神 【免费下载链接】PuzzleSolver 一款针对CTF竞赛MISC的工具~ 项目地址: https://gitcode.com/gh_mirrors/pu/PuzzleSolver 你是不是每次参加CTF比赛,看到MISC题目就头疼&#xff1f…...

qmc-decoder深度解析:高效解密QQ音乐加密格式的技术架构与实践

qmc-decoder深度解析:高效解密QQ音乐加密格式的技术架构与实践 【免费下载链接】qmc-decoder Fastest & best convert qmc 2 mp3 | flac tools 项目地址: https://gitcode.com/gh_mirrors/qm/qmc-decoder 在数字音乐版权保护的背景下,QQ音乐采…...

3步掌握终极AMD Ryzen调试工具:免费解锁硬件深层控制

3步掌握终极AMD Ryzen调试工具:免费解锁硬件深层控制 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://git…...

Chatbox终极主题定制指南:打造专属AI交互界面

Chatbox终极主题定制指南:打造专属AI交互界面 【免费下载链接】chatbox Powerful AI Client 项目地址: https://gitcode.com/GitHub_Trending/ch/chatbox 你是否曾经在深夜编码时,被刺眼的浅色界面晃得眼睛发疼?又或者,你是…...

大语言模型驱动的定性研究编码自动化:GATOS工作流实践指南

1. 项目概述:当大语言模型遇见定性研究编码如果你做过定性研究,比如分析几百份开放式问卷、访谈转录稿,或者处理海量的用户反馈,你肯定对“编码”这个环节又爱又恨。爱的是,它能将杂乱无章的文本转化为结构化的见解&am…...

终极解决方案:如何用qmc-decoder快速解锁QQ音乐加密格式

终极解决方案:如何用qmc-decoder快速解锁QQ音乐加密格式 【免费下载链接】qmc-decoder Fastest & best convert qmc 2 mp3 | flac tools 项目地址: https://gitcode.com/gh_mirrors/qm/qmc-decoder 你是否曾经下载了QQ音乐,却发现那些.qmc3、…...

如何用DeepL Chrome翻译插件打破语言障碍:从安装到精通的完整指南

如何用DeepL Chrome翻译插件打破语言障碍:从安装到精通的完整指南 【免费下载链接】deepl-chrome-extension A DeepL Translator Chrome extension 项目地址: https://gitcode.com/gh_mirrors/de/deepl-chrome-extension 你是否经常遇到需要阅读外文网页却苦…...