当前位置:网站首页>“天上天下,唯我独尊”——单例模式
“天上天下,唯我独尊”——单例模式
2022-07-24 16:17:00 【用户6557940】
引言
你能在电脑上调出两个Windows任务管理器吗?假设能,如果两个管理器显示的数据相同,那何必要存在两个呢?如果两个管理器显示的数据不同,那我该相信哪一个呢?
试试看,应该有且仅有一个吧?一个系统里有且仅有一个Windows任务管理器实例供外界访问 。如何保证系统里有且仅有一个实例对象呢?并且能够供外界访问?你可以在系统里定义一个统一的全局变量,但这并不能防止创建多个对象(想一想,为什么?)这就是单例模式的典型应用。
对于一个软件系统中的某些类来说,只有一个实例很重要。假设Windows系统上可以同时调出两个Windows任务管理器,这两个任务管理器显示的都是同样的信息,那势必造成内存资源的浪费;如果这两个任务管理器显示的是不同的信息,这也给用户带来了困惑,到底哪一个才是真实的状态?
01
单例模式简介
单例模式定义:
单例模式:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。
单例模式有3个要点:
- 这个类只能有一个实例;
- 它必须自己创建这个实例;
- 它必须自己向整个系统提供这个实例。
02
单例模式结构
单例模式结构非常简单,其UML图如下所示,只包含一个类,即单例类。为防止创建多个对象,其构造函数必须是私有的(外界不能访问)。另一方面,为了提供一个全局访问点来访问该唯一实例,单例类提供了一个公有方法getInstance来返回该实例。
03
单例模式代码及效果
3.1.单例模式代码及验证
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
#include <iostream>
#include <string.h>
using namespace std;
//抽象产品类AbstractBall
class Singleton
{
public:
static Singleton* getInstance(){
if (instance == NULL){
printf("创建新的实例\n");
instance = new Singleton();
}
return instance;
}
private:
Singleton(){}
static Singleton* instance;
};
Singleton* Singleton::instance = NULL;
#endif //__SINGLETON_H__可以看到,构造函数是私有的(private),即单例模式对象只能在类内部实例化(这就满足了单例模式的第二个要点)。同时,实例对象instance是静态static的,也就是全局的,假设客户端实例化了两个Singleton,但instance只有一个(这就满足了第一个要点)。那第三个要点如何满足呢?即外界如何获取单例对象呢?上述代码中定义了一个方法(同样也是static的)getInstance().
接下来看看客户端如何使用:
int main()
{
Singleton *s1 = Singleton::getInstance();
Singleton *s2 = Singleton::getInstance();
system("pause");
return 0;
}运行结果如下图:
难道单例模式就这么简单的就ok了吗?
no no no! too young too simple!
3.2.多线程环境测试单例模式
上述的客户端验证似乎说明上述代码实现了单例模式。的确是实现了,但是这样真的安全吗?试想在多线程环境里,当两个线程(甚至更多线程)同时使用,同样存在创建了多个实例的隐患。下面的代码是在多线程环境下进行的测试:
/*非线程安全 单例模式*/
#include <process.h>
#include <Windows.h>
#define THREAD_NUM 5
unsigned int __stdcall CallSingleton(void *pPM)
{
Singleton *s = Singleton::getInstance();
int nThreadNum = *(int *)pPM;
Sleep(50);
printf("线程编号为%d\n", nThreadNum);
return 0;
}
int main()
{
HANDLE handle[THREAD_NUM];
//线程编号
int threadNum = 0;
while (threadNum < THREAD_NUM)
{
handle[threadNum] = (HANDLE)_beginthreadex(NULL, 0, CallSingleton, &threadNum, 0, NULL);
//等子线程接收到参数时主线程可能改变了这个i的值
threadNum++;
}
//保证子线程已全部运行结束
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
system("pause");
return 0;
}一共创建了5个线程,每个线程里面都试图创建一个单例对象。理论上,最终只有第一个线程(第一个被系统调度的线程)才能打印出“创建新的实例”,然而:
上述结果说明,3.1的单例模式的代码并不是线程安全的。
3.3.线程安全的单例模式代码实现
如何做到线程安全呢?多线程同步与互斥有多重方法,这里Jungle介绍互斥锁这种用法。代码如下:
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
#include <iostream>
#include <string.h>
#include <mutex>
using namespace std;
class Singleton
{
public:
static Singleton* getInstance(){
if (instance == NULL){
m_mutex.lock();
if (instance == NULL){
printf("创建新的实例\n");
instance = new Singleton();
}
m_mutex.unlock();
}
return instance;
}
private:
Singleton(){}
static Singleton* instance;
static std::mutex m_mutex;
};
Singleton* Singleton::instance = NULL;
std::mutex Singleton::m_mutex;
#endif //__SINGLETON_H__接下来再在多线程环境下测试,结果显示功能正常!
04
单例模式总结
优点:
- 单例模式提供了严格的对唯一实例的创建和访问
- 单例模式的实现可以节省系统资源
缺点:
- 如果某个实例负责多重职责但又必须实例唯一,那单例类的职责过多,这违背了单一职责原则
- 多线程下需要考虑线程安全机制
- 单例模式没有抽象层,不方便扩展
适用环境:
- 系统只需要一个实例对象
- 某个实例只允许有一个访问接口
边栏推荐
- 电话系统规则
- C# TCP客户端窗体应用程序异步接收方式
- 安信证券开户在手机开户安全吗?
- Knowledge points of MySQL (12)
- Urban safety series popular science - enter the high incidence period of drowning, if you know the common sense of life-saving
- Array in PHP_ The pit of merge
- yolov4 训练自己的数据集
- How to generate complex flow chart of XMIND
- Dynamics 365: explain virtual entity from 0 to 1
- 如何在 PHP 中防止 XSS
猜你喜欢
![Programming in CoDeSys to realize serial communication [based on raspberry pie 4B]](/img/9c/05118efe2152dc5461a92720732076.jpg)
Programming in CoDeSys to realize serial communication [based on raspberry pie 4B]

TCP协议调试工具TcpEngine V1.3.0使用教程

Parse string

About SQL data query statements
![Dynamics crm: [problem solved]cannot open SQL encryption symmetric key because symmetric key password](/img/ae/125fcb16c9d85714c7bbd1255d1d18.png)
Dynamics crm: [problem solved]cannot open SQL encryption symmetric key because symmetric key password

Dynamics crm: sharing records for users and teams

C TCP client form application asynchronous receiving mode

如何防止跨站点脚本 (XSS) 攻击完整指南

Caikeng Alibaba cloud Kex_ exchange_ identification: read: Connection reset by peer

Adaptive design and responsive design
随机推荐
[leetcode] day102 spiral matrix II
leetcode:162. 寻找峰值【二分寻找峰值】
[leetcode] day103 search two-dimensional matrix II
From which dimensions can we judge the quality of code? How to have the ability to write high-quality code?
Windows10 installation free redis
Dynamics crm: sharing records for users and teams
Getting started with OpenMP
做完数据治理,质量依旧很差
How to prevent XSS in PHP
yolov3 训练自己的数据集
TCP协议调试工具TcpEngine V1.3.0使用教程
Arduino IDE ESP32固件安装和升级教程
微调LayoutLM v3进行票据数据的处理和内容识别
Knowledge points of MySQL (12)
Chapter 2 using API Mgmnt service
.net review the old and know the new: [6] what is LINQ
Configuring WAPI certificate security policy for Huawei wireless devices
Machine learning notes - building a recommendation system (5) feedforward neural network for collaborative filtering
收益率在百分之六以上的理财产品,请说一下
Code shoe set - mt2095 · zigzag jump