2023-6-12-第三式单例模式

news2024/9/17 7:22:43


🍿*★,°*:.☆( ̄▽ ̄)/$:*.°★* 🍿

💥💥💥欢迎来到🤞汤姆🤞的csdn博文💥💥💥
💟💟喜欢的朋友可以关注一下,下次更新不迷路💟💟
😆😆😆私聊获取个人订阅号哦,欢迎订阅共同学习😆😆😆
💖💖💖💖可以加入大家庭群聊,一起学习天天有福利💖💖💖💖





🍬本文摘要

在这里插入图片描述

设计方法二十三式之单例模式


目录

  • 🍬本文摘要
  • 😉一、基础概念
  • 🐱‍🐉二、单例模式实现
  • 🎉三、单例模式与线程安全
  • 🐱‍🚀四、懒汉模式怎么保证线程安全
  • 🎂五、饿汉模式怎么保证线程安全
  • 🥩六、注意事项
  • 🍳参考文献
  • 🧊文章总结



😉一、基础概念

单例模式(Singleton Pattern)是一种创建型设计模式,它保证某一个类只有一个实例存在,并提供一个全局访问点。

在单例模式中,通过将类的构造函数设为私有,从而防止外部直接创建对象。同时,通过一个静态方法或者变量来获取该类的唯一实例。如果该类的实例不存在,则创建一个新的实例并返回;如果该类的实例已经存在,则直接返回该实例。

单例模式的优点是可以避免因为创建多个实例而导致的资源浪费和性能下降。同时,由于该类只有一个实例存在,因此可以更好地控制该实例的状态和行为。此外,单例模式还可以提供一个全局访问点,使得其他对象可以方便地访问该实例。

需要注意的是,单例模式虽然可以解决某些问题,但也可能会引入新的问题。例如,单例模式可能会导致代码的耦合性增加,使得代码难以测试和维护。因此,在使用单例模式时需要权衡利弊,合理使用。


🐱‍🐉二、单例模式实现

懒汉模式

class Singleton {
public:
    static Singleton* getInstance() {
        static Singleton instance;
        return &instance;
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,Singleton 类的构造函数是私有的,因此外部无法直接创建该类的实例。getInstance() 方法是该类的静态方法,用于获取该类的唯一实例。在 getInstance() 方法中,使用了静态局部变量的方式来创建该类的唯一实例。由于静态局部变量只会在第一次调用时初始化,因此可以保证该类只有一个实例存在。

需要注意的是,在多线程环境下,需要考虑线程安全问题。可以使用加锁的方式来解决该问题。此外,在使用单例模式时,还需要避免使用全局变量或静态变量等方式来创建对象,这样会破坏单例模式的原则。

饿汉模式

class Singleton {
public:
    static Singleton* getInstance() {
        return &instance;
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton instance;
};

Singleton Singleton::instance;

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,Singleton 类的唯一实例是在类定义中直接创建的,因此可以保证在程序启动时就已经创建了该实例,即所谓的“饿汉模式”。需要注意的是,由于该实例是在程序启动时就创建的,因此可能会影响程序启动的速度和内存占用。

在使用饿汉单例模式时,需要注意线程安全问题。如果该类的实例需要在多线程环境下使用,需要使用加锁的方式来保证线程安全。


🎉三、单例模式与线程安全

单例模式的懒汉模式和饿汉模式都可能会造成线程安全问题,具体原因如下:

  1. 懒汉模式

懒汉模式是指在第一次使用时才创建单例对象。在多线程环境下,如果多个线程同时调用 getInstance() 方法,那么可能会创建多个实例,违反了单例模式的原则。例如,当一个线程在判断实例是否存在时,还没有创建实例,此时另一个线程也进入了判断实例是否存在的代码块,也未创建实例,然后两个线程都创建了实例,导致单例模式失效。

  1. 饿汉模式

饿汉模式是指在程序启动时就创建单例对象。在多线程环境下,如果多个线程同时调用 getInstance() 方法,那么可能会访问到未完全初始化的实例,从而导致程序异常或者崩溃。例如,当一个线程正在初始化实例时,另一个线程也调用了 getInstance() 方法,此时可能会访问到未完全初始化的实例。

因此,在实际使用中,需要考虑单例模式的线程安全问题。可以使用加锁的方式来保证线程安全,或者采用其他线程安全的方式来实现单例模式。


🐱‍🚀四、懒汉模式怎么保证线程安全

如果要求在懒汉模式下保证线程安全,并且实例化之后不能再加锁,可以使用双重检查锁定(Double-Checked Locking)的方式来实现。双重检查锁定是一种常用的单例模式实现方式,它可以在保证线程安全的同时,避免了每次调用 getInstance() 方法都需要加锁的性能问题。

具体来说,双重检查锁定的实现方式如下:

class Singleton {
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);

            if (instance == nullptr) {
                instance = new Singleton();
            }
        }

        return instance;
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton* instance;
    static std::mutex mutex;
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,getInstance() 方法首先判断实例是否已经存在,如果不存在,则获取互斥锁,然后再次判断实例是否已经存在。由于在获取互斥锁之前已经进行了一次判断,因此可以避免多个线程同时获取互斥锁的情况,从而提高了程序的性能。

需要注意的是,在使用双重检查锁定时,需要注意内存可见性问题。由于编译器和处理器的优化,可能会导致变量的值在不同的线程中不一致,从而导致程序出现异常。可以使用 std::atomic 类型来解决这个问题,或者使用 C++11 中引入的跨平台内存屏障(Memory Barrier)来保证内存可见性。


🎂五、饿汉模式怎么保证线程安全

饿汉模式是指在程序启动时就创建单例对象。在这种情况下,由于实例已经被创建,因此不存在多个线程同时创建实例的问题。所以,饿汉模式本身就是线程安全的。

但是,如果在程序启动时需要进行大量的初始化工作,可能会影响程序的启动速度。因此,可以考虑将初始化工作延迟到实例被第一次使用时再进行,这样可以避免不必要的初始化工作,提高程序的启动速度。

如果需要在饿汉模式下延迟初始化工作,可以使用静态成员变量和静态成员函数的方式来实现。静态成员变量只会在第一次使用时被初始化,因此可以实现延迟初始化的效果。示例代码如下:

class Singleton {
public:
    static Singleton* getInstance() {
        return &getInstanceImpl();
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton& getInstanceImpl() {
        static Singleton instance;
        return instance;
    }
};

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,getInstanceImpl() 方法返回的是一个静态局部变量 instance 的引用。由于静态局部变量只会在第一次使用时被初始化,因此可以实现延迟初始化的效果。在 getInstance() 方法中,直接返回 getInstanceImpl() 方法的返回值即可。

需要注意的是,在使用静态局部变量时,需要考虑线程安全问题。在多线程环境下,可能会出现多个线程同时访问静态局部变量的情况,从而导致程序出现异常。可以使用 C++11 中引入的线程安全的局部静态变量(Thread-safe Local Static)来解决这个问题。


🥩六、注意事项

单例模式是一种常用的设计模式,但是在使用时需要注意以下几点:

  1. 线程安全性:单例模式在多线程环境下需要保证线程安全性,否则可能会导致多个线程同时创建实例的问题。可以使用懒汉模式、饿汉模式、双重检查锁定等方式来保证线程安全性。

  2. 内存泄漏:单例模式在程序运行期间只会创建一个实例,如果实例没有被正确释放,可能会导致内存泄漏问题。可以使用智能指针等方式来避免内存泄漏问题。

  3. 可维护性:单例模式可能会导致代码的可维护性下降,因为单例模式隐藏了对象的创建和销毁过程,使得代码的调试和修改变得困难。因此,在使用单例模式时需要注意代码的可维护性。

  4. 应用场景:单例模式适用于需要全局唯一实例的场景,例如配置文件、日志系统等。但是,在一些场景下,单例模式可能会导致代码的复杂性增加,因此需要根据实际情况来选择是否使用单例模式。

  5. 可测试性:单例模式可能会导致代码的可测试性下降,因为单例模式隐藏了对象的创建和销毁过程,使得代码的测试变得困难。因此,在使用单例模式时需要注意代码的可测试性。


🍳参考文献

🧊文章总结

提示:这里对文章进行总结:

  本文讲了单例模式的一些注意事项,希望大家学习后有所收获。






更多好文推荐

🍸2021-4月Python 机器学习——中文新闻文本标题分类
🍹2021年4月-(计算机网络)小型校园网络模拟搭建,最全最准确版
🍺2022-10-31-基于用户的协同过滤推荐算法实现+MAE+RMSE
🍻2022-11-28-大数据可视化,特征维度大于50
🥂2023-3-9-一篇简短的文章把C++左右值关系讲的透透彻彻

上一篇
End
下一篇

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

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

相关文章

前端vue可以左右滚动的切换的tabs tabs选项卡 滑动动画效果 自动宽度

前端vue可以左右滚动的切换的tabs tabs选项卡 滑动动画效果 自动宽度, 下载完整代码请访问https://ext.dcloud.net.cn/plugin?id13003 效果图如下: #### 使用方法 使用方法 swiperTabList: ["2023-06-10","2023-06-11","2023-06-12","2…

ICASSP 2023丨基于 CTC 的模型改进,实现更强的模型结构

分享一篇网易智企易盾 AI Lab 团队在ICASSP 2023被收录的语音识别方向的论文《Improving CTC-based ASR Models with Gated Interplayer Collaboration&#xff08;基于 CTC 的模型改进&#xff0c;实现更强的模型结构&#xff09;》 论文地址&#xff1a;https://arxiv.org/ab…

UnityVR--组件10--UGUI简单介绍

目录 前言 UI基础组件 1. Canvas 2. EventSystem 3. Image 4. Text/TextMeshPro/InputField 5. Button控件 其他 前言 UGUI是Unity推出的新的UI系统&#xff0c;它与Unity引擎结合得更紧密&#xff0c;并拥有强大的屏幕自适应和更简单的深度处理机制&#xff0c;更容易使用和…

从零开始:安装H2数据库的步骤解析

在开发或编写示例时&#xff0c;有时需要用到数据库&#xff0c;如果本机上刚好没有安装&#xff0c;类似有些同学是临时借用的电脑或学校的电脑刚好没有安装时&#xff0c;我们可以使用H2数据库来快速代替&#xff0c;即方便又灵活。 安装和使用H2数据库的步骤如下&#xff1a…

【vue3】11-Vue 3中的Composition Api(一)

Vue3 - composition Api 前言options Api的弊端 认识composition ApiSet up函数的基本使用set up函数的参数set up函数的返回值 Set up中数据的响应式1. Reactive API2. Ref API3. ref和reactive的开发 setup中的其他函数&#xff08;了解&#xff09;setup中禁用this 前言 op…

Ansible自动化运维工具的初步了解及模块使用

1.Ansible的相关知识 1.1 Ansible工具的了解 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。Ansible能…

tp6安装并使用rabbitMQ

最近因为业务需要,要用到MQ就去研究了一下,说实话,安装环境给我搞自闭了,大概是我太菜 刚开始使用yum换源,各种安装卸载始终找不到自己要用的版本,后来全部卸载,下载安装包 编译安装解百忧 我用的是erlang 25.3 的版本,MQ使用的是3.11.3的版本,符合官方要求,这里的版本是有强…

智能果树叶部疾病识别实践,基于HOG+SVM和轻量级的CNN模型分别开发构建苹果果树叶部疾病识别分析系统

在农业领域中&#xff0c;将机器学习和深度学习模型结合应用有着广泛的实用意义&#xff0c;在这块相关的实践中以往的项目开发实践则是主要集中在农作物种植相关的&#xff0c;今天本文的主要目的就是开发构建果树叶部疾病识别分析系统&#xff0c;这里是以苹果果树叶部疾病数…

springcloud环境搭建

目录 1、创建一个父项目 ​编辑 2、创建子项目 2.1创建订单系统-order ​编辑 2.2创建库存系统-stock 3、创建rest服务 3.1添加web依赖 3.2编写controller 3.3订单中需要调用库存中的扣减库存的接口 通过idea开发工具进行搭建 1、创建一个父项目 通过spring initializr…

Django基础入门:后台管理 超级用户 数据表注册操作③

Django基础入门&#xff1a;后台管理 超级用户 数据表注册操作③ Django Admin数据表后台管理超级用户数据表注册admin操作日志 &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &#x1f396;️&#x1f396;️:Python领域新星创作者&#xff0c;CSDN实力新星…

使用randoop自动化生成测试用例

1. 综述 使用randoop自动化生成测试用例 2. 操作 下载randoop的jar包&#xff1b;将jar包和所要测试的类&#xff08;如Calculate.class&#xff09;放在同一个文件夹&#xff1b;在此文件夹打开命令行提示符&#xff0c;输入如下命令&#xff1a; java -ea -classpath .;ra…

嵌入式Linux驱动开发 05:阻塞与非阻塞

文章目录 目的基础说明开发准备驱动程序应用程序 O_NONBLOCK应用程序驱动程序程序演示 poll应用程序驱动程序程序演示 异步通知应用程序驱动程序程序演示 总结 目的 不管在应用开发还是驱动开发中阻塞和非阻塞都是绕不开的话题。这篇文章将介绍相关的基础内容。 这篇文章中内…

Redis命令-认识NoSQl和Redis常见的通用命令

1. 认识NoSQL 非关系型数据库 NoSQL是指一类非关系型数据库&#xff0c;它们采用的数据模型不同于传统的关系模型&#xff0c;它通常使用键值对、文档、图形等非传统的数据结构进行数据存储&#xff0c;不遵循预定义的模式和模型。NoSQL数据库通常分布式、高可扩展性&#xff0…

理解Java ThreadLocal

原文链接 理解Java ThreadLocal ThreadLocal是Java提供的为每个线程存储线程独立的数据的存储方式&#xff0c;也就是说用ThreadLocal来保存的数据&#xff0c;只能被当前线程所访问&#xff0c;其他线程无法访问&#xff0c;因为只有&#xff08;一个线程&#xff09;当前线程…

六级备考6天|CET-6|听力第五六讲|6月11日|19:44~20:07

目录 第五讲 第六讲 第五讲 第六讲 ​​​​​​​

支付宝认证的作用是什么?考试方向有哪些?

通过考证书来提升自己的能力&#xff0c;是现在的打工人经常会做的事&#xff0c;而对于从事信息通信技术行业的人来说&#xff0c;可以选择的证书非常多&#xff0c;其中支付宝认证是一个新设立的证书&#xff0c;但是实用性非常高。支付宝作为我们生活中常用的软件&#xff0…

arthas 的初使用

Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法执行耗时&#xff0c;类加载信…

PD QC诱骗取电应用IC《乐得瑞LDR6328S》广泛应用于各大小家电

随着现在智能家居的应用越来越广泛&#xff0c;带电池的产品一天比一天多&#xff0c;今天这篇文章就来讲一下那些支持快速充电(PD QC)的产品应用电路是怎么实现的 USB PD受电端取电芯片&#xff08;乐得瑞取电受电)LDR6328S 支持多协议快充取电&#xff01; 1、概述 LDR6328S…

推荐5款你可能没见过的效率软件

你有没有想过&#xff0c;有些软件能让你的电脑用起来更方便&#xff0c;更快&#xff0c;更好看&#xff1f;这篇文章就为你介绍了五款这样的软件&#xff0c;它们分别是BreeZip&#xff0c;ClipClip&#xff0c;燃精灵&#xff0c;Sticky Notes和Tabby。下面我们来看看它们都…

增强 Kubernetes 可观测性:API Server Tracing 特性已到达 Beta 版本

标题 在分布式系统中&#xff0c;很难弄清楚问题在哪里。 想象一个场景&#xff0c;这也是 Kubernetes 集群管理员最常遇到的问题&#xff0c;Pod 无法正常启动&#xff0c;这时候作为管理员&#xff0c;我们会先去思考这可能是哪个组件出了问题&#xff0c;然后去对应的组件查…