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

文章目录

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

前提

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

单例模式

概念

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

应用场景

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

  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.mfbz.cn/a/577582.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ中的交换机类型

交换机类型 可以看到&#xff0c;在订阅模型中&#xff0c;多了一个exchange角色&#xff0c;而且过程略有变化&#xff1a; Publisher&#xff1a;生产者&#xff0c;不再发送消息到队列中&#xff0c;而是发给交换机 Exchange&#xff1a;交换机&#xff0c;一方面&#xff…

03 后端入参校验:自定义注解实现

03 后端入参校验&#xff1a;自定义注解实现 一、前言二、实现1、新建Spring Boot项目2、引入依赖3、新建注解类4、新建校验器5、全局异常处理器6、编写Controller7、新建实体类8、启动并测试 一、前言 在 Java 后端开发中&#xff0c;为了实现入参校验&#xff0c;常常会使用…

【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门

【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门 文章目录 【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门1. 概述2. 服务熔断服务降级(CircuitBreaker)2.1 案例说明2.1.1 基于计数的滑动窗口2.1.2 测试2.2.1 基于时间的滑动窗口2.2.2 测试 3. 隔离(B…

多行Textview 计算切分后的长度,并回退长度

实现类似的效果&#xff0c;一个多行的 textview&#xff0c; 如果赋值一个超长的字符&#xff0c;尾部长度回退部分&#xff0c;并添加 ... 最后添加一个详情按钮。 如果不超长则不显示详情 效果如图&#xff1a; 获取截断之后的字符长度 fun getLimitedCharacterCount(textV…

更新!!!Unity移动端游戏性能优化简谱

UWA官方出品&#xff0c;结合多年优化经验撰写了《Unity移动端游戏性能优化简谱》&#xff0c;文章从Unity移动端游戏优化的一些基础讨论出发&#xff0c;例举和分析了近几年基于Unity开发的移动端游戏项目中最为常见的部分性能问题&#xff0c;并展示了如何使用UWA的性能检测工…

Java web应用性能分析之【6种OOM监控和分析】

Java web应用性能分析之【Linux服务器性能监控分析概叙】-CSDN博客 Java web应用性能分析概叙-CSDN博客 Java web应用性能分析之【基准测试】-CSDN博客 Java web应用性能分析之【sysbench基准测试】-CSDN博客 Java web应用性能分析之【CPU飙升分析概述】-CSDN博客 Java we…

GPT学术优化推荐(gpt_academic )

GPT学术优化 (GPT Academic):支持一键润色、一键中英互译、一键代码解释、chat分析报告生成、PDF论文全文翻译功能、互联网信息聚合GPT等等 ChatGPT/GLM提供图形交互界面&#xff0c;特别优化论文阅读/润色/写作体验&#xff0c;模块化设计&#xff0c;支持自定义快捷按钮&…

014_用vim复制粘贴_保持双手正位

[oeasy]python0014_用vim复制粘贴_保持双手正位 继续运行 &#x1f94a; 回忆上次内容 程序员 还是 很可爱的 要关心 身边的程序员 啊 毕竟是新时代的 典型新职业 文明 主流职业 血型 渔猎采集文明 猎人 O 游牧文明 牧民 B 农业文明 农民 A 工业文明 工人 商…

Linux——DNS的配置和使用

一、DNS 域名服务器&#xff0c;实现IP和域名的转换 DNS 协议运行在 UDP 协议之上&#xff0c;使用端口号 53 2.结构 DNS 的命名空间的结构如下&#xff1a; 1. 根域名&#xff08; Root Domain &#xff09;&#xff1a; 根域名位于 DNS 命名空间的顶部&#xff0c;它表示…

【继承和多态】

闭上眼睛&#xff0c;什么都不听.............................................................................................................. 文章目录 前言 一、【继承】 1.1【继承的概念】 1.2【 继承的定义】 1.2.1【定义格式】 1.2.2【继承关系和访问限定符】 1.2…

浏览器的同源策略与解决跨域

同源策略&#xff08;协议、域名、端口&#xff09; 同源策略&#xff08;Same-Origin Policy&#xff09;是一个在浏览器安全模型中被实施的重要安全机制。它是基于域名、协议和端口号的限制&#xff0c;用于防止不同源的网页间的恶意行为和信息泄露。 根据同源策略&#xf…

探秘Java线程:从概念到实践

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

Unity Timeline学习笔记(4) - 自定义轨道OnCreateClip和CreateTrackMixer用法上的区分

前面我们第二篇文章Unity Timeline学习笔记(2) - PlayableTrack是一个初步的PlayableTrack使用方法&#xff0c;有时候可能会个性化定制专属轨道。 OnCreateClip的例子 下面我们做一个例子&#xff1a; 首先是轨道 //FeatureTrack.cs using System.ComponentModel; using U…

以太网口硬件知识分享

一、了解网口通信基本原理 实现网络通信实质上是PHY与MAC及RJ45接口实现信号传输。MAC 就是以太网控制器&#xff0c;MAC属于数据链路层&#xff0c;主要负责把数据封装成帧&#xff0c;对帧进行界定实现帧同步。对MAC地址和源MAC地址及逆行相应的处理并对错误帧进行处理。PHY…

JavaScript-3(内置对象+数组对象+字符串对象)

目录 1.预解析 2.对象 什么是对象 创建对象的三种方法 利用字面量创建方法 利用new Object创建对象 构造函数创建对象 new关键字 遍历对象 3.内置对象 Math对象 Math概述 Math随机数 Date日期对象 格式化日期 Date总的时间毫秒 4.数组对象 创建数组的两种方式…

进销存单机版和excel进销存那个好用

进销存单机版和EXCEL进销存哪个好用&#xff1f;单机版是安装在单台电脑上使用的&#xff0c;它不能像网络版一样可以多台电脑同时共享数据&#xff0c;所以进销存单机版有一个优势就是不需要连接网络也可以使用。 进销存单机版 进销存软件单机版是经过开发人员设计好的一种信…

网页提示语闪太快的定位问题(selenium)

selenium UI自动化时&#xff0c;提示语闪太快&#xff0c;导致无法获取元素的问题 解决办法 步骤一&#xff1a; F12---》控制台输入debugger 步骤二&#xff1a;对于需要定位的部分&#xff0c;在控制台的debugger处回车&#xff0c;可以定住页面 步骤三&#xff1a;正常定…

生成式AI原理技术详解(一)——神经网络与深度学习

本文主要介绍了生成式AI的最新发展&#xff0c;提到了GPT-5和AI软件工程师在行业中的影响&#xff0c;指出AI技术进步对国家竞争和个人职业发展的潜在影响。 未来已来 最近有两则新闻&#xff1a; sam altman自曝GPT-5细节&#xff0c;公开宣称GPT-5提升将非常大&#xff0c;任…

62、回溯-N皇后

思路&#xff1a; N皇后问题要求在一个nn的棋盘上放置n个皇后&#xff0c;使得它们不能相互攻击。皇后可以攻击同一行、同一列&#xff0c;以及两个对角线方向上的其他皇后。解决这个问题意味着找到所有可能的棋盘配置&#xff0c;每个配置都符合上述条件。 1、初始化数据结构…

Docker 入门篇(二)-- Linux 环境离线安装

引言 docker 系列文章&#xff1a; Docker 入门篇&#xff08;一&#xff09;-- 简介与安装教程&#xff08;Windows和Linux&#xff09; 一、安装环境准备 centos &#xff1a;CentOS Linux release 7.6.1810 (Core)docker 版本&#xff1a;docker-26.1.0.tgz 官网下载地址…
最新文章