实现线程安全的单例模式
在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式。单例模式分为懒汉模式,跟饿汉模式两种。
首先给出饿汉模式的实现
template <class T> class singleton { protected:singleton(){}; private:singleton(const singleton&){};//禁止拷贝singleton& operator=(const singleton&){};//禁止赋值static T* m_instance; public:static T* GetInstance(); };template <class T> T* singleton<T>::GetInstance() {return m_instance; }template <class T> T* singleton<T>::m_instance = new T();
在实例化m_instance 变量时,直接调用类的构造函数。顾名思义,在还未使用变量时,已经对m_instance进行赋值,就像很饥饿的感觉。这种模式,在多线程环境下肯定是线程安全的,因为不存在多线程实例化的问题。
下面来看懒汉模式
template <class T> class singleton { protected:singleton(){}; private:singleton(const singleton&){};singleton& operator=(const singleton&){};static T* m_instance; public:static T* GetInstance(); };template <class T> T* singleton<T>::GetInstance() {if( m_instance == NULL){ m_instance = new T();}return m_instance; }template <class T> T* singleton<T>::m_instance = NULL;
懒汉模式下,在定义m_instance变量时先等于NULL,在调用GetInstance()方法时,在判断是否要赋值。这种模式,并非是线程安全的,因为多个线程同时调用GetInstance()方法,就可能导致有产生多个实例。要实现线程安全,就必须加锁。
下面给出改进之后的代码
template <class T> class singleton { protected:singleton(){}; private:singleton(const singleton&){};singleton& operator=(const singleton&){};static T* m_instance;static pthread_mutex_t mutex; public:static T* GetInstance(); };template <class T> T* singleton<T>::GetInstance() {pthread_mutex_lock(&mutex);if( m_instance == NULL){ m_instance = new T();}pthread_mutex_unlock(&mutex);return m_instance; }template <class T> pthread_mutex_t singleton<T>::mutex = PTHREAD_MUTEX_INITIALIZER;template <class T> T* singleton<T>::m_instance = NULL;
这一切看起来都很完美,但是程序猿是一种天生就不知道满足的动物。他们发现GetInstance()方法,每次进来都要加锁,会影响效率。然而这并不是必须的,于是又对GetInstance()方法进行改进
template <class T> T* singleton<T>::GetInstance() {if( m_instance == NULL){pthread_mutex_lock(&mutex);if( m_instance == NULL){ m_instance = new T();}pthread_mutex_unlock(&mutex);}return m_instance; }
这也就是所谓的“双检锁”机制。但是有人质疑这种实现还是有问题,在执行 m_instance = new T()时,可能 类T还没有初始化完成,m_instance 就已经有值了。这样会导致另外一个调用GetInstance()方法的线程,获取到还未初始化完成的m_instance 指针,如果去使用它,会有意料不到的后果。其实,解决方法也很简单,用一个局部变量过渡下即可:
template <class T> T* singleton<T>::GetInstance() {if( m_instance == NULL){pthread_mutex_lock(&mutex);if( m_instance == NULL){ T* ptmp = new T();m_instance = ptmp;}pthread_mutex_unlock(&mutex);}return m_instance; }
到这里在懒汉模式下,也就可以保证线程安全了。
然而,在linux下面还有另一种实现。linux提供了一个叫pthread_once()的函数,它保证在一个进程中,某个函数只被执行一次。下面是使用pthread_once实现的线程安全的懒汉单例模式
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
上面的单例类使用了模板,对每一种类型的变量都能实例化出唯一的一个实例。
例如要实例化一个int类型
int *p = singleton<int>::GetInstance()
例如要实例化一个string类型
string *p = singleton<string>::GetInstance()
在上面的实现中,在实例化对象时,调用GetInstance()函数时都没有传递参数,这是犹豫不同的对象其初始化时参数个数都不一样。如果要支持不同类型的对象带参数初始化,则需要重载GetInstance函数。然而在c++11中,已经支持了可变参数函数。这里给出一个简单的例子
#ifndef _SINGLETON_H_ #define _SINGLETON_H_template <class T> class singleton { protected:singleton(){}; private:singleton(const singleton&){};singleton& operator=(const singleton&){};static T* m_instance; public:template <typename... Args>static T* GetInstance(Args&&... args){if(m_instance == NULL)m_instance = new T(std::forward<Args>(args)...);return m_instance;}static void DestroyInstance(){if(m_instance )delete m_instance;m_instance = NULL;} };template <class T> T* singleton<T>::m_instance = NULL;#endif
测试函数
#include <iostream> #include <string> #include "singleton.h"using namespace std; struct A {A(int a ,int b):_a(a),_b(b){}int _a;int _b; };int main() {int *p1 = singleton<int>::GetInstance(5);int *p2 = singleton<int>::GetInstance(10);cout << *p1 << " " << *p2 <<endl;string *p3 = singleton<string>::GetInstance("aa");string *p4 = singleton<string>::GetInstance("bb");cout << *p3 << " " << *p4 <<endl;A *p5 = singleton<A>::GetInstance(1,2);A *p6 = singleton<A>::GetInstance(4,5);cout << p5->_a << " " << p6->_a<<endl;return 0; }
运行结果如下
实现线程安全的单例模式相关推荐
- Android之线程安全的单例模式,Adapter注意事项之引用传值
线程安全的单例模式 单位模式一般写法如下: public static FestivalLab mInstance; private FestivalLab() { } public static F ...
- java 静态内部类 线程安全问题_单例模式的七种写法, 面试题:线程安全的单例模式...
http://cantellow.iteye.com/blog/838473 http://meizhi.iteye.com/blog/537563 第一种(懒汉,线程不安全): Java代码 pu ...
- C++实现线程安全的单例模式
C++实现线程安全的单例模式 1.单例模式 一个简单的单例模式很容易实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实 ...
- 生产者消费者模型、信号量、线程池以及单例模式的实现
生产者消费者模型!!---对典型的应用场景设计的解决方案 生产者与消费者模型应用场景:有线程不断的生产数据,有线程不断的处理数据. 数据的生产与数据的处理:放在同一个线程中完成,因为执行流只有一个,那 ...
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
百度众说纷纭的情况下就不如自己写例子测试理论,话不多说,上代码: public class Sta {public static long date=System.currentTimeMillis( ...
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式懒加载
原贴:https://www.cnblogs.com/suncoolcat/p/3362230.html 写的很详细,需细细品味. 百度众说纷纭的情况下就不如自己写例子测试理论,话不多说,上代码: p ...
- JAVA的多线程、死锁、线程间通信、如何规避死锁、线程安全的单例模式
主要内容: 多线程 线程和进程间的关系 Java中的线程理论 Java中线程类的实现方式 Java中线程的常用方法 线程安全性问题 线程间通信 线程的死锁 如何规避死锁 线程安全的单例模式 多线程 线 ...
- C#中线程安全的单例模式
一.单例模式 ①单例模式的定义:是用来保证这个类在运行期间只会被创建一个实例,并提供一个访问它的全局访问点. ②单例模式的作用:保证一个类只有一个访问实例,节省系统资源. ③单例模式的特点:私有构造方 ...
- java线程池使用单例模式
一.线程池不使用单例模式 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;publ ...
- C++ static 变量和线程安全和单例模式
static变量初始化顺序 1.1 全局变量.文件域的static变量和类的static成员变量在main函数执行之前初始化 1.2 局部静态变量在第一次被使用时初始化 static变量的线程安全 2 ...
最新文章
- python 获取excel文件内sheet名称列表
- 李德毅:“反用驾驶脑”测认知能力,谁说酒驾一定违规?
- 组播基本概念、IGMP、IGMP监听学习笔记
- 材料科学中的数据挖掘:晶体图神经网络解读与代码解析
- Timus1286(欧几里德算法的应用)
- width用计算机英语,计算机的英语词汇
- SAP Cloud for Customer的CTI呼叫中心解决方案
- 计算机系统的备份与还原实验报告,数据库《数据备份与还原》实验报告.doc
- 2013-11-11 Oracle 课堂测试 练习题 例:BULK COLLECT及return table
- 在 phpMyAdmin 里添加新用户帐号
- Java多线程学习二十五:阻塞和非阻塞队列的并发安全原理||如何选择适合自己的阻塞队列?
- GB/T19001—2008质量管理体系要求、标准、贯标(贯彻标准)
- 电商项目5:商品模块
- html 渐变背景色,渐变文字颜色
- 离散数学_量词分配等值式
- bigquery json处理函数json_extract和json_extract_scalar的区别
- 【CSS基础】文字垂直居中
- 27岁,今年研究生上岸,有什么忠告可以给我的?
- 推荐一个免费的PDF在线编辑网站
- 艾德莱斯绸:“千年时尚”托起新产业
热门文章
- 一文教你如何高效使用 IDEA !
- 皮一皮:男生的求生欲,以及神回复...
- 免费版的 IDEA 为啥不能使用 Tomcat ?
- 公司用了 6 年的分布式锁,很是牛逼啊!
- Java枚举原来还能这么用
- php 静态类在worker,GatewayWorker的Events.php中调用Worker::runAll()出现异常
- CSS样式----块级元素和行内元素
- pybind11向C++ dll 传递数组 图像
- torch.where用法
- pytorch优化器,学习率衰减学习笔记