实现一个线程安全的单例模式

来源:互联网 发布:随机生成域名检测 编辑:程序博客网 时间:2024/06/06 03:14

一、单例模式

       单例模式也叫单件模式。Singleton是一个非常常用的设计模式,几乎所有稍微大一些的程序都会使用它,所以构建一个高效的Singleton很重要。

1、单例类保证全局只有一个唯一实例对象

2、单例类提供获取这个唯一实例的接口。

        我们都能想到一个简单的单例模式该怎样实现:构造函数声明为private或protec防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。

先看一段代码

class Singleton{public://获取唯一对象实例的接口函数static Singleton* GetInstance(){if(_sInstance==NULL){_sInstance=new Singleton();}return _sInstance;}//删除示例对象static void DelInstance(){if(_sInstance){delete _sInstance;_sInstance=NULL;}}void Print(){cout<<_data<<endl;}private://构造函数定义为私有,限制只能在类内创建对象Singleton():_data(0){}Singleton(const Singleton&);Singleton& operator=(const Singleton&);//指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例static Singleton* _sInstance;//单例类里面的数据int _data;}void TestSingleton(){TestSingleton::GetInstance()->Print();TestSingleton::DelInstance;}
        这个方法简单易懂,但是它是线程不安全的,如果两个线程同时调用_sInstance方法且同时检测到p是NULL值,则两个线程会同时构造一个实例给p,这是严重的错误。当然我们也有线程安全的实现方法。

二、懒汉模式与饿汉模式
       单例大约有两种实现方法:懒汉与饿汉。

       *懒汉:顾名思义,不到万不得已就不回去实例化,也就是说在第一次用到类实例的时候才回去实例化。

       *饿汉:与懒汉相反,它是饥不择食的,所以在单例类定义的时候就进行实例化。

        两者的特点与选择要点:

       *由于要进行线程同步,所以在访问量较大,或者可能访问的线程较多时采用饿汉实现,可以实现更好的性能。这是以空间换时间。

       *在访问量较小时,可以实现懒汉模式。这是以时间换空间。

三、线程安全的的懒汉模式

       *方法一:加锁的经典懒汉实现:

class singleton{protected:singleton(){pthread_mutex_init(&mutex);}private:static singleton* p;public:static pthread_mutex_t mutex;static singleton* instance();};pthread_mutex_t singleton::mutex;singleton* singleton::p=NULL;singleton* singleton::instance(){if(p==NULL){pthread_mutex_lock(&mutex);if(p==NULL){p=new singleton();}pthread_mutex_unlock(&mutex);}return p;}
       *方法二:内部静态变量的懒汉实现

这个方法也比较简单,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。

class singleton{protected:singleton(){pthread_mutex_init(&mutex);}public:static pthread_mutex_t mutex;static singleton* instance();int a;};pthread_mutex_t singleton::mutex;singleton* singleton::instance(){pthread_mutex_lock(&mutex);static singleton obj;pthread_mutex_unlock(&mutex);return &obj;}

四、饿汉模式

        线程安全本来就是安全的,不用加锁,因为在饿汉模式下,在单例类定义时就已经定义了一个对象,对类进行了初始化。后面不管哪个线程调用函数instance(),都只不过时返回一个对象的指针而已。所以是线程安全的,不需要在成员函数instance中加锁。

class singleton{protected:singleton(){}private:static singleton* p;public:static singleton* initance();int a;};singleton* singleton::p=new singleton;singleton* singleton::initance(){return p;}int main(){singleton* s=singleton::instance();s->a=10;return 0;}