在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式。单例模式分为懒汉模式,跟饿汉模式两种。

首先给出饿汉模式的实现

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;
}

运行结果如下

实现线程安全的单例模式相关推荐

  1. Android之线程安全的单例模式,Adapter注意事项之引用传值

    线程安全的单例模式 单位模式一般写法如下: public static FestivalLab mInstance; private FestivalLab() { } public static F ...

  2. java 静态内部类 线程安全问题_单例模式的七种写法, 面试题:线程安全的单例模式...

    http://cantellow.iteye.com/blog/838473 http://meizhi.iteye.com/blog/537563 第一种(懒汉,线程不安全): Java代码  pu ...

  3. C++实现线程安全的单例模式

    C++实现线程安全的单例模式 1.单例模式 一个简单的单例模式很容易实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实 ...

  4. 生产者消费者模型、信号量、线程池以及单例模式的实现

    生产者消费者模型!!---对典型的应用场景设计的解决方案 生产者与消费者模型应用场景:有线程不断的生产数据,有线程不断的处理数据. 数据的生产与数据的处理:放在同一个线程中完成,因为执行流只有一个,那 ...

  5. 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式

    百度众说纷纭的情况下就不如自己写例子测试理论,话不多说,上代码: public class Sta {public static long date=System.currentTimeMillis( ...

  6. 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式懒加载

    原贴:https://www.cnblogs.com/suncoolcat/p/3362230.html 写的很详细,需细细品味. 百度众说纷纭的情况下就不如自己写例子测试理论,话不多说,上代码: p ...

  7. JAVA的多线程、死锁、线程间通信、如何规避死锁、线程安全的单例模式

    主要内容: 多线程 线程和进程间的关系 Java中的线程理论 Java中线程类的实现方式 Java中线程的常用方法 线程安全性问题 线程间通信 线程的死锁 如何规避死锁 线程安全的单例模式 多线程 线 ...

  8. C#中线程安全的单例模式

    一.单例模式 ①单例模式的定义:是用来保证这个类在运行期间只会被创建一个实例,并提供一个访问它的全局访问点. ②单例模式的作用:保证一个类只有一个访问实例,节省系统资源. ③单例模式的特点:私有构造方 ...

  9. java线程池使用单例模式

    一.线程池不使用单例模式 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;publ ...

  10. C++ static 变量和线程安全和单例模式

    static变量初始化顺序 1.1 全局变量.文件域的static变量和类的static成员变量在main函数执行之前初始化 1.2 局部静态变量在第一次被使用时初始化 static变量的线程安全 2 ...

最新文章

  1. python 获取excel文件内sheet名称列表
  2. 李德毅:“反用驾驶脑”测认知能力,谁说酒驾一定违规?
  3. 组播基本概念、IGMP、IGMP监听学习笔记
  4. 材料科学中的数据挖掘:晶体图神经网络解读与代码解析
  5. Timus1286(欧几里德算法的应用)
  6. width用计算机英语,计算机的英语词汇
  7. SAP Cloud for Customer的CTI呼叫中心解决方案
  8. 计算机系统的备份与还原实验报告,数据库《数据备份与还原》实验报告.doc
  9. 2013-11-11 Oracle 课堂测试 练习题 例:BULK COLLECT及return table
  10. 在 phpMyAdmin 里添加新用户帐号
  11. Java多线程学习二十五:阻塞和非阻塞队列的并发安全原理||如何选择适合自己的阻塞队列?
  12. GB/T19001—2008质量管理体系要求、标准、贯标(贯彻标准)
  13. 电商项目5:商品模块
  14. html 渐变背景色,渐变文字颜色
  15. 离散数学_量词分配等值式
  16. bigquery json处理函数json_extract和json_extract_scalar的区别
  17. 【CSS基础】文字垂直居中
  18. 27岁,今年研究生上岸,有什么忠告可以给我的?
  19. 推荐一个免费的PDF在线编辑网站
  20. 艾德莱斯绸:“千年时尚”托起新产业

热门文章

  1. 一文教你如何高效使用 IDEA !
  2. 皮一皮:男生的求生欲,以及神回复...
  3. 免费版的 IDEA 为啥不能使用 Tomcat ?
  4. 公司用了 6 年的分布式锁,很是牛逼啊!
  5. Java枚举原来还能这么用
  6. php 静态类在worker,GatewayWorker的Events.php中调用Worker::runAll()出现异常
  7. CSS样式----块级元素和行内元素
  8. pybind11向C++ dll 传递数组 图像
  9. torch.where用法
  10. pytorch优化器,学习率衰减学习笔记