【单例模式】

概述

一个类不管创建多少次对象,永远只能得到该类型的一个对象的实例。

常用到的比如日志模块 ,数据库模块

    饿汉:在类加载时就创建单例对象,因此它是线程安全的,因为对象的创建在程序启动时就已经完成,不存在多线程同时创建对象的问题。
懒汉:在第一次使用单例对象时才创建对象,这种方式在多线程环境下需要考虑线程安全问题,通常使用互斥锁来保证对象只被创建一次。

1. 一个私有构造函数(确保只能单例类自己创建实例):
单例类通常会将其构造函数设为私有,以防止外部代码直接实例化对象。

2. 一个私有静态变量(确保只有一个实例)
单例类通常包含一个私有的静态变量,用于保存该类的唯一实例。

3. 一个公有静态函数(给使用者提供调用方法)

单例模式的6种实现

1、懒汉式(线程不安全)

class Singleton {
private:// 延迟初始化:初始为 nullptr,第一次使用时创建static Singleton* instance;// 私有构造:禁止外部 newSingleton() {}// 禁用拷贝(防止复制实例)Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:// 全局访问点static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton(); // 第一次调用时创建实例}return instance;}
};// 静态成员类外初始化
Singleton* Singleton::instance = nullptr;

先不创建实例,当第一次被调用时,再创建实例,所以被称为懒汉式。

优点:延迟了实例化,如果不需要使用该类,就不会被实例化,只有在需要时才创建实例,避免了资源浪费。

缺点:线程不安全,多线程环境下,如果多个线程同时进入了` if (instance == null) `,若此时还未实例化,也就是`instance == null`,那么就会有多个线程执行 `instance = new Singleton(); `,就会实例化多个实例;

2、饿汉式(线程安全)

class Singleton {
private:static Singleton instance;Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:static Singleton& getInstance() {return instance;}
};Singleton Singleton::instance;      // 静态成员类外初始化(程序启动时执行)

先不管需不需要使用这个实例,直接先实例化好实例(饿死鬼一样,所以称为饿汉式),然后当需要使用的时候,直接调方法就可以使用了

优点:提前实例化好了一个实例,避免了线程不安全问题的出现,

缺点:直接实例化了实例,不再延迟实例化;若系统没有使用这个实例,或者系统运行很久之后才需要使用这个实例,都会使操作系统的资源浪费。

3、懒汉式(线程安全)

class Singleton {
private:static Singleton* instance;static std::mutex mtx; // 互斥锁:保证线程安全Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:// 整个方法加锁:每次调用都需获取锁static Singleton* getInstance() {std::lock_guard<std::mutex> lock(mtx); // 自动加锁/解锁,避免死锁if (instance == nullptr) {instance = new Singleton();}return instance;}
};// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

实现和线程不安全的懒汉式 几乎一样,唯一不同的点是,在get方法上 加了一把锁。如此一来,多个线程访问,每次只有拿到锁的的线程能够进入该方法,避免了多线程不安全问题的出现。

优点:延迟实例化,节约了资源,并且是线程安全的。

缺点:虽然解决了线程安全问题,但是性能降低了。因为,即使实例已经实例化了,既后续不会再出现线程安全问题了,但是锁还在,每次还是只能拿到锁的线程进入该方法使线程阻塞,等待时间过长。

4、双重检查锁实现(线程安全)

class Singleton {
private:// volatile确保多线程下实例状态可见static volatile Singleton* instance;static std::mutex mtx;Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:// 双重检查锁定实现线程安全的懒加载static Singleton* getInstance() {// 第一次检查:避免频繁加锁if (instance == nullptr) {// 加锁:确保只有一个线程进入初始化std::lock_guard<std::mutex> lock(mtx);// 第二次检查:防止多线程同时通过第一次检查if (instance == nullptr) {instance = new Singleton();}}return const_cast<Singleton*>(uniqueInstance);}
};// 静态成员初始化
volatile Singleton* Singleton::uniqueInstance = nullptr;
std::mutex Singleton::mtx;

双重检查锁相当于是改进了线程安全的懒汉式。线程安全的懒汉式的缺点是性能降低了,造成的原因是因为即使实例已经实例化,依然每次都会有锁。

而现在,我们将锁的位置变了,并且多加了一个检查。也就是,先判断实例是否已经存在,若已经存在了,则不会执行判断方法内的有锁方法了。 而如果,还没有实例化的时候,多个线程进去了,也没有事,因为里面的方法有锁,只会让一个线程进入最内层方法并实例化实例。如此一来,最多最多,也就是第一次实例化的时候,会有线程阻塞的情况,后续便不会再有线程阻塞的问题。

volatile 的作用

`new Singleton()` 底层分 3 步:分配对象 -> 初始化对象 -> 指针指向内存

编译器/CPU可能重排上述顺序,1->3->2,导致B拿到未初始化的实例,`volatile` 禁止重排,确保“初始化完成后才能赋值指针”,同时保证对线程对`instance`的读写可见

5、静态内部类的实现

class Singleton {
private:// 私有构造Singleton() {}// 禁用拷贝Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 静态内部类:仅在被访问时加载static class SingletonHolder {public:// 内部类静态成员:程序启动时初始化(线程安全)static Singleton instance;};public:// 全局访问点:调用时触发内部类加载static Singleton& getInstance() {return SingletonHolder::instance;}
};// 静态内部类的静态成员初始化
Singleton Singleton::SingletonHolder::instance;

延迟加载:外部类 `Singleton` 加载时,内部类 `SingletonHolder` 不加载;仅调用 `getInstance()` 时,内部类才加载并初始化 `instance`。
线程安全:C++ 静态成员初始化在单线程阶段执行,天然避免并发问题

缺点:无法主动销毁实例(实例随程序退出释放)

6、枚举类(线程安全 )

// 枚举类:默认线程安全,且天然防止拷贝
enum class Singleton {INSTANCE; // 唯一枚举实例// 枚举类成员方法(扩展功能)void doSomething() {std::cout << "Singleton 执行任务" << std::endl;}
};// 全局访问宏(可选,简化调用)
#define SINGLETON Singleton::INSTANCE

枚举类的三个特点:
1. 成员唯一:枚举里的每个成员(INSTANCE)是全局唯一的,整个程序只有一个,不能像普通类那样new多个对现象
2. 禁止拷贝
3. 自动初始化:枚举的成员会在程序启动时自动初始化(饿汉),无需手动创建

使用场景

(1)频繁实例化然后又销毁的对象,使用单例模式可以提高性能

(2)经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏

(3)使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信

具体如:日志,数据库连接池,计数器等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94559.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity开发如何实现换装技术

一、3D换装方案SkinnedMeshRenderer组件替换&#xff08;最常用&#xff09;适用场景&#xff1a;角色需要保持骨骼动画&#xff0c;更换服装/武器等实现步骤&#xff1a;1.准备模型&#xff1a;所有服装需使用相同骨骼结构&#xff08;建议在建模软件中绑定到同一套骨骼&#…

RabbitMQ面试精讲 Day 29:版本升级与平滑迁移

【RabbitMQ面试精讲 Day 29】版本升级与平滑迁移 在“RabbitMQ面试精讲”系列的第29天&#xff0c;我们聚焦于一个在中高级系统架构与运维面试中极具分量的话题——RabbitMQ的版本升级与平滑迁移。随着业务发展和RabbitMQ自身功能演进&#xff08;如从经典集群到Quorum队列、从…

Python-机器学习概述

​​一、人工智能三大概念​​ ​​人工智能&#xff08;AI&#xff09;​​ 定义&#xff1a;使用计算机模拟或代替人类智能的研究领域 目标&#xff1a;像人类一样思考&#xff08;理性推理&#xff09;、行动&#xff08;决策执行&#xff09; 别名&#xff1a;仿智 ​​…

GIT压缩提交,将多个已经push的commit提交,合并成一个

1.选中要合并的提交2.选中后右键选着Squash Committs3.重新编辑提交信息4.操作完成后不能pull,要强制pushgit push --force

(多线程)线程安全和线程不安全 产生的原因 synchronized关键字 synchronized可重入特性死锁 如何避免死锁 内存可见性

线程安全问题产生原因 线程安全问题主要发生在多线程环境下&#xff0c;当多个线程同时访问共享资源时&#xff0c; 如果没有采取适当的同步措施&#xff0c;就可能导致数据不一致或程序行为异常1.[根本]操作系统对于线程的调度是随机的.抢占式执行&#xff0c;这是线程安全问题…

defineCustomElement 的局限性及重载需求分析

一、defineCustomElement 的核心局限性 Vue 的 defineCustomElement 虽然实现了 Vue 组件到 Web Components 的转换,但在跨框架/跨语言场景下存在以下关键局限,这也是你的项目需要重载其返回构造器的根本原因: 1. 框架间事件模型不兼容 Vue 事件机制:依赖 $emit 转换的 C…

如何在前端开发中应用AI技术?

一、AI 辅助前端开发流程&#xff08;提效工具&#xff09;智能代码生成与补全使用 AI 编程工具&#xff08;如 GitHub Copilot、Cursor、Amazon CodeWhisperer&#xff09;实时生成代码片段&#xff0c;支持 HTML、CSS、JavaScript、React/Vue 等框架语法。例如&#xff0c;输…

极海发布APM32F425/427系列高性能MCU:助力工业应用升级

聚焦工业4.0及能源管理应用对主控MCU的高性能需求&#xff0c;极海正式发布APM32F425/427系列高性能拓展型MCU&#xff0c;集合运算性能、ADC性能、Flash控制器性能与通信接口四大维度革新&#xff0c;进一步增强了EMC性能&#xff0c;重新定义Cortex-M4F内核在复杂工业场景下的…

JSX深度解析:不是HTML,胜似HTML的语法糖

JSX深度解析&#xff1a;不是HTML&#xff0c;胜似HTML的语法糖 作者&#xff1a;码力无边大家好&#xff01;我是依然在代码世界里乘风破浪的码力无边。欢迎回到我们的《React奇妙之旅》第二站&#xff01; 在上一篇文章中&#xff0c;我们成功地用Vite启动了第一个React应用&…

大模型应用新趋势:从思维链到 HTML 渲染的破局之路

一、大模型交互范式的演进&#xff1a;从 Prompt 工程到思维链革新早期的 Prompt 工程曾面临 “模型特异性” 困境 —— 精心设计的提示词在不同模型上效果迥异。但随着 ** 思维链&#xff08;CoT&#xff09;** 技术的成熟&#xff0c;这一局面正在改变。从 OpenAI o1 的隐式整…

从“找不到”到“秒上手”:金仓文档系统重构记

你是否曾在浩如烟海的产品手册中迷失方向&#xff1f;是否为了一个关键参数翻遍十几页冗余说明&#xff1f;是否对时灵时不灵的搜索功能感到抓狂&#xff1f;甚至因为漫长的加载时间而失去耐心&#xff1f;我们懂你!这些曾困扰金仓用户的文档痛点&#xff0c;从现在起&#xff…

【开源项目分享】可监控电脑CPU、显卡、内存等硬件的温度、功率和使用情况

系列文章目录 【开源项目分享】可监控电脑CPU、显卡、内存等硬件的温度、功率和使用情况 &#xff08;一&#xff09;开源的硬件监控工具 LibreHardwareMonitor &#xff08;二&#xff09;LibreHardwareMonitor 分层架构设计 &#xff08;三&#xff09;LibreHardwareMonitor…

帕累托优化:多目标决策的智慧与艺术

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 在相互冲突的目标中寻找最优平衡 ✨ 1. 帕累托优化概述 帕累托优化&a…

#Linux内存管理学以致用# 请你根据linux 内核struct page 结构体的双字对齐的设计思想,设计一个类似的结构体

Linux struct page 的双字对齐设计思想1.双字对齐&#xff08;8字节对齐&#xff09;&#xff1a;确保struct page的大小是sizeof(long)的整数倍&#xff08;通常8字节&#xff09;&#xff0c;便于CPU高效访问。减少内存碎片&#xff0c;提高缓存行&#xff08;Cache Line&…

白酒变局,透视酒企穿越周期之道

今年以来&#xff0c;在科技股的带动下&#xff0c;A股市场表现十分突出&#xff0c;近期沪指甚至创出了十年来新高。然而&#xff0c;在这轮市场的表现中&#xff0c;曾经被资金热捧的白酒板块&#xff0c;却显得有些沉寂。业绩层面&#xff0c;从目前已披露的白酒上市公司半年…

智慧园区:从技术赋能到价值重构,解锁园区运营新范式

在数字化浪潮席卷产业的当下&#xff0c;智慧园区已从 “概念蓝图” 落地为 “实战方案”&#xff0c;其核心逻辑既源于技术的突破性应用&#xff0c;也扎根于企业的实际需求&#xff0c;更顺应着行业发展的未来趋势&#xff0c;成为驱动园区从传统管理向智能化运营升级的核心引…

模运算(密码学/算法)

1 什么是模运算 模运算的概念 模运算是一种算术运算&#xff0c;常写作a mod n&#xff0c;表示整数a除以正整数n后的余数。 模数是模运算中的除数n&#xff0c;它决定了结果的范围。 公式表达&#xff1a; 对于任意整数a和正整数n&#xff0c;可以将a表示为&#xff1a;a qn …

海康相机的 HB 模式功能详解

海康相机的 HB 模式是一种无损压缩技术,全称为High Bandwidth 模式,主要用于提升工业相机在高速场景下的数据传输效率。其核心原理是通过硬件级无损压缩算法对原始图像数据进行压缩,在不损失画质的前提下减少数据量,从而突破千兆网络的带宽限制,实现更高的行频和传输帧率。…

electron应用开发:命令npm install electron的执行逻辑

我们来彻底解析 npm install electron 这个命令背后的完整执行逻辑。这是一个非常精妙的过程&#xff0c;远不止下载一个简单的 JavaScript 包那么简单。理解了它&#xff0c;你就能透彻地明白 Electron 开发环境的运作原理&#xff0c;并能轻松解决各种安装问题。 npm instal…

Visual Studio 2022不同项目设置不同背景图

ClaudiaIDE Visual Studio 地址&#xff1a;https://marketplace.visualstudio.com/items?itemNamekbuchi.ClaudiaIDE&ssrfalse#overviewgithub 地址&#xff1a;https://github.com/buchizo/ClaudiaIDE/ 这是一个Visual Studio扩展&#xff0c;可以让你设置自定义背景图…