设计模式入门(三)单例模式

文章目录

  • 前提
  • 单例模式
    • 概念
    • 应用场景
    • 应用
    • 懒汉式
    • 饿汉式
  • 参考链接

前提

最近在实际项目中使用到了设计模式中的单例模式,之前也单纯地从理论方面学习过单例模式,但是一直没有机会实际应用到项目中,这次从项目入手简单地对单例模式进行总结。

单例模式

概念

单例模式,顾名思义,就是在代码中一个类只有一个实例
它的思想就是: 如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。

应用场景

单例模式是一种常见的设计模式,适用于以下场景:

  1. 全局资源管理:当应用程序需要一个全局资源(例如日志记录器、配置管理器、数据库连接等)时,可以使用单例模式来确保只有一个实例存在,并且所有部分都共享该实例。
  2. 线程池:在多线程环境中,使用单例模式来管理线程池是一种常见的做法。线程池是一种重要的资源,通过单例模式可以确保所有线程共享相同的线程池实例,并能够在需要时方便地访问。
  3. 缓存管理:在需要使用缓存的情况下,可以使用单例模式来管理缓存。通过单例模式,可以确保只有一个缓存实例存在,所有部分都可以访问并共享该实例,从而提高缓存的利用率和性能。
  4. 日志记录:在应用程序中,日志记录是一项非常重要的功能。使用单例模式来管理日志记录器可以确保所有部分都使用相同的日志记录器实例,并且可以方便地记录应用程序的状态和行为。
  5. 配置管理:在应用程序中,通常需要加载和管理配置信息。使用单例模式来管理配置信息可以确保所有部分都使用相同的配置实例,并且可以方便地访问和修改配置信息。
    总的来说,单例模式适用于需要确保只有一个实例存在,并且所有部分都可以方便地访问和共享该实例的场景。

我这次的应用场景是:程序中有一组寄存器要维护(寄存器在实际中有且只有一组),无论何时我访问这组寄存器,这组寄存器的值和上一次我访问是的值要一样,不会发生变换(除非用户去手动去改变)。

应用

单例模式有两种实现方式:懒汉式和饿汉式
懒汉式和饿汉式的区别就在于:

  1. 懒汉式只有当用户请求实例化对象时,才会初始化对象;而饿汉式在程序一旦运行后,就会自动初始化对象。这就会带来利弊:如果在整个程序运行过程中,我们不需要实例化对象,那么懒汉式就不会初始化对象,也就不会申请额外的空间资源;而饿汉式会自动初始化对象,而整个程序运行过程中我们没有用到这个对象,就会带来额外的空间以及资源开销。这一点在接下来的代码中可以仔细体会。
  2. 而有了上面的这一点可得,没有加锁的懒汉式是线程不安全的,而饿汉式是线程安全的。具体就说:多个线程如果同一时刻要初始化对象,而这个对象只能有一个,那么该听谁的呢?这样的话,每个线程可能都会初始化对象,那么这个单例模式就不是唯一的了。而饿汉式因为程序一开始就会初始化对象,所以不存在这种情况。

懒汉式

所有单例的实现都包含以下两个相同的步骤:

  1. 将默认构造函数设为私有, 防止其他对象使用单例类的 new运算符。
  2. 新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。

具体来说,就是要有以下要求:

  1. 在类中添加一个私有静态成员变量用于保存单例实例
  2. 声明一个公有静态构建方法用于获取单例实例
  3. 在静态方法中实现"延迟初始化"(懒汉式)。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例
  4. 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用

懒汉式代码:

#define N 100
class Singleton
{
public:static Singleton* GetInstance(){if (instance_ == nullptr) {printf("只需要申请一次对象\n");instance_ = new Singleton;}return instance_;}private:Singleton() {register_ = (char*)malloc(sizeof(char) * N);}~Singleton();Singleton(const Singleton& other) = delete;  // 没有拷贝构造Singleton& operator=(const Singleton& other) = delete;  // 没有拷贝赋值static Singleton* instance_;char* register_;
};Singleton* Singleton::instance_ = nullptr;  // 类的静态成员变量需要类外初始化int main()
{Singleton* regA = Singleton::GetInstance();Singleton* regB = Singleton::GetInstance();return 0;
}

线程安全的懒汉式:

#define N 100
class Singleton
{
public:static Singleton* GetInstance(){if (instance_ == nullptr) {  i_mutex.lock();  // 先加上锁if (instance_ == nullptr)  // 再次判断是否为nullptr,因为可能有其他的线程抢占{printf("第一次申请对象\n");instance_ = new Singleton();}i_mutex.unlock();}return instance_; }char* GetPtr(){return register_;}
private:Singleton() {printf("调用构造函数");register_ = (char*)malloc(sizeof(char) * N);}~Singleton();Singleton(const Singleton& other) = delete;  // 拷贝构造Singleton& operator=(const Singleton& other) = delete;  // 拷贝赋值static Singleton* instance_;static std::mutex i_mutex;char* register_;
};std::mutex Singleton::i_mutex;
Singleton* Singleton::instance_ = nullptr;void Thread1() {// Following code emulates slow initialization.//std::this_thread::sleep_for(std::chrono::milliseconds(1000));Singleton* singleton = Singleton::GetInstance();std::cout << (void*)singleton->GetPtr() << "\n";
}void Thread2() {// Following code emulates slow initialization.std::this_thread::sleep_for(std::chrono::milliseconds(1000));Singleton* singleton = Singleton::GetInstance();std::cout << (void*)singleton->GetPtr() << "\n";
}int main()
{std::thread t1(Thread1);t1.detach();std::thread t2(Thread2);t2.join();return 0;
}

饿汉式

饿汉式在程序启动时直接初始化对象,申请资源。因此不用“延迟初始化”(延迟初始化在这里的意思就是:只有我们调用了GetInstance()方法时,才会申请资源)。

#define N 100
class Singleton
{
public:static Singleton* GetInstance(){return instance_;}private:Singleton() {printf("调用构造函数");register_ = (char*)malloc(sizeof(char) * N);}~Singleton();Singleton(const Singleton& other) = delete;  // 拷贝构造Singleton& operator=(const Singleton& other) = delete;  // 拷贝赋值static Singleton* instance_;char* register_;
};Singleton* Singleton::instance_ = new Singleton; // 直接申请int main()
{Singleton* regA = Singleton::GetInstance();Singleton* regB = Singleton::GetInstance();return 0;
}

参考链接

  1. https://refactoringguru.cn/design-patterns/singleton

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

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

相关文章

jdk版本冲突,java.lang.UnsupportedClassVersionError: JVMCFRE003

主要是编辑器所用的jdk版本和项目用的不一致导致的&#xff0c;虽然编译通过了&#xff0c;但是运行是会报错 选好后点击Apply点击ok&#xff0c;然后重新编译一遍项目就可以了

【运维】Gitlab备份

Gitlab备份 备份什么&#xff1f;在哪&#xff1f;备份命令&#xff1f; 一、配置文件 GitLab默认的配置文件路径&#xff1a;/etc/gitlab/ /etc/gitlab/gitlab.rb&#xff1a;主配置文件&#xff0c;包含外部URL、仓库目录、备份目录等 /etc/gitlab/gitlab-secrets.json&…

万辰集团如何破局“增收不增利”的困境?

在波澜不惊的食用菌加工行业&#xff0c;万辰集团&#xff08;300972.SZ&#xff09;曾是一名平凡的参与者。2021年战略转型的号角吹响&#xff0c;万辰集团挥别了传统业务&#xff0c;转而投身于快速增长的量贩零食市场&#xff0c;并迅速扩张到成为这一领域的重要玩家。万辰的…

Docker常见问题排查思路与实战

Docker作为一种流行的容器化技术&#xff0c;已经在众多场景中得到广泛应用。然而&#xff0c;在使用过程中&#xff0c;我们难免会遇到各种问题。本文将介绍一些常见的Docker问题及其排查思路&#xff0c;并通过实战案例帮助大家更好地理解和应对这些挑战。 1. Docker容器启动…

又重新搭了个个人博客

哈喽大家好&#xff0c;我是咸鱼。 前段时间看到一个学弟写了篇用 Hexo 搭建博客的教程&#xff0c;心中沉寂已久的激情重新被点燃起来。&#xff08;以前搞过一个个人网站&#xff0c;但是因为种种原因最后不了了之&#xff09; 于是花了一天时间参考教程搭了个博客网站&…

【数据结构(邓俊辉)学习笔记】向量03——无序向量

文章目录 0.概述1.元素访问2.置乱器3.判等器与比较器4.无序查找4.1 判等器4.2 顺序查找4.3 实现4.4 复杂度 5. 插入5.1 算法实现5.2 复杂度分析 6. 删除6.1 区间删除6.2 单元删除6.3 复杂度 7. 唯一化7.1 实现7.2 正确性7.3 复杂度 8. 遍历8.1 实现8.2 复杂度 9. 总结 0.概述 …

Spark 基础

/* Why Spark一、MapReduce编程模型的局限性1、繁杂&#xff1a;只有Map和Reduce两个操作&#xff0c;复杂的逻辑需要大量的样板代码2、处理效率低&#xff1a;2.1、Map中间结果写磁盘&#xff0c;Reduce写HDFS&#xff0c;多个Map通过HDFS交换数据2.2、任务调度与启动开销大3、…

LayuiMini使用时候初始化模板修改(下载源码)

忘记加了 下载 地址 &#xff1a; layui-mini: layuimini&#xff0c;后台admin前端模板&#xff0c;基于 layui 编写的最简洁、易用的后台框架模板。只需提供一个接口就直接初始化整个框架&#xff0c;无需复杂操作。 LayuiMini使用时候初始化模板官网给的是&#xff1a; layu…

SpringBoot整合RabbitMQ direct交换机、fanout交换机、topic交换机

PS 常见错误 1、有匹配到交换机&#xff0c;但是没有匹配到绑定的队列。&#xff08;交换机没有绑定队列&#xff09;- not route 2、没有匹配到交换机。&#xff08;交换机名称错误&#xff0c;not found - exchange&#xff09; 3、交换机和队列都没有匹配&#xff08;和第二…

Vue 组件单元测试深度探索:组件交互与状态变更 专业解析和实践

在Vue组件单元测试中&#xff0c;验证组件之间的交互&#xff08;如父组件与子组件、兄弟组件之间的通信&#xff09;以及状态变更的正确性对于保证整个应用的协调运作至关重要。本文详细介绍了父组件向子组件传递props、子组件向父组件发送事件、兄弟组件通过共享状态&#xf…

自然语言处理 (NLP) 的技术演变史

一、简述 本文的目标是了解自然语言处理 (NLP) 的历史&#xff0c;包括 Transformer 体系结构如何彻底改变该领域并帮助我们创建大型语言模型 (LLM)。 基础模型&#xff08;如 GPT-4&#xff09;是最先进的自然语言处理模型&#xff0c;旨在理解、生成人类语言并与之交互。 要理…

鸿蒙小案例-搜索高亮

搜索高亮目前官方也没有可以现成的组件&#xff0c;但是需求来了&#xff0c;怎么办&#xff0c;只能摸索着自己写一个 目前官方API中最接近的应该是 richText组件了&#xff0c;富文本组件&#xff0c;当然可以实现&#xff0c;但是有不少问题 1.大小调整太麻烦&#xff0c;跟…

MySQL中截取字符串有哪些方法

文章目录 一、SUBSTRING() 或 SUBSTR() 函数二、LEFT() 函数三、RIGHT() 函数四、使用字符串连接和定位函数截取五、 正则表达式截取六、SUBSTRING_INDEX() 函数&#xff1a; 在MySQL中&#xff0c;你可以使用多种方法来截取字符串。以下是一些常用的方法&#xff1a; 一、SUB…

焊接机器人-常见焊接工艺参数

常见焊接工艺参数 常见焊缝平焊立焊 常见焊接工艺调试方法注意事项 常见焊缝 常见的焊缝一般见于不需要坡口焊的规则钢构件&#xff1a;如H型钢、H型牛角杠、T型梁、弧形梁等。 平焊 参数Value电流(安培)200A电压(伏特 )20V摆弧-振幅(毫米)4-6mm摆弧- 频率(Hz)1Hz摆弧- 两侧…

国产3D自研技术如何突围?眸瑞科技给3D建设、管理带来全新模式

眸瑞科技是全球领先的数字孪生引擎技术及服务提供商&#xff0c;它专注于让一切3D模型在全网多端轻量化处理与展示&#xff0c;为行业数字化转型升级与数字孪生应用提供成套的国产自研3D可视化技术、产品与服务。 引言 眸瑞科技是全球领先的数字孪生引擎技术及服务提供商&…

【MyBatisPlus】一、公共字段填充配置

目录 一、实体类配置 二、配置MyBatis Plus元对象处理器 三、接口字段自动填充 在使用mybatisplus项目中设置公共字段填充&#xff0c;可以按如下进行配置 一、实体类配置 TableField(value "create_time",fill FieldFill.INSERT)private LocalDateTime createTime…

【C++】哈希思想

目录 哈希介绍&#xff1a; 一&#xff0c;位图 1-1&#xff0c;位图的认识 1-2&#xff0c;位图的简单实现 1-3&#xff0c;位图的应用 二&#xff0c;布隆过滤器 2-1&#xff0c;布隆过滤器的认识 2-2&#xff0c;布隆过滤器的简单实现 2-3&#xff0c;布隆过滤器的…

Kafka 3.x.x 入门到精通(06)——Kafka进阶

Kafka 3.x.x 入门到精通&#xff08;06&#xff09;&#x1f449;&#x1f449;&#x1f449;&#x1f449; Kafka进阶 3. Kafka进阶3.1 Controller选举3.2 Broker上线下线3.3 数据偏移量定位3.4 Topic删除3.5 日志清理和压缩3.7 页缓存3.8 零拷贝3.9 顺写日志3.10 Linux集群部…

Debian12使用宝塔国际aaPanel无法安装Docker

宝塔国际aaPanel自带安装Docker&#xff0c;安装了几次都失败&#xff0c;最后仔细看了安装日志&#xff0c;才发现其中的问题。 复制 --2023-11-28 13:42:13-- https://node.aapanel.com/install/0/docker_install_en.sh Resolving node.aapanel.com (node.aapanel.com)...…

Dockerfile镜像构建实战

一、构建Apache镜像 cd /opt/ #建立工作目录 mkdir /opt/apache cd apache/vim Dockerfile #基于的基础镜像 FROM centos:7 #维护镜像的用户信息 MAINTAINER this is apache image <cyj> #镜像操作指令安装Apache软件 RUN yum install -y httpd #开启80端口 EXPOSE 80 #…