单例模式是众多模式中理解起来最为简单的,因为这个模式就是为了创建一个单独且不可复制的对象。因此,对于单例模式也就不需要进行过多的抽象形式的描述了,因为本身理解难度并不大!加上,最近在项目中,对于一些功能的添加,还使用到了单例模式,因此,对于单例模式的应用相对要熟悉一点。
举个例子:
#pragma once
class SingleTon
{
private:
SingleTon(void);
public:
//用以控制生成对象的过程
static SingleTon*Instance();
~SingleTon(void);
private:
staticSingleTon *instance; //用以判断是否已经存在对象
};
#include "SingleTon.h"
#include <iostream>
using namespace std;
SingleTon* SingleTon::instance =NULL;
SingleTon::SingleTon(void)
{
cout<<"thisis a singleton"<<endl;
}
SingleTon::~SingleTon(void)
{
}
SingleTon* SingleTon::Instance()
{
if(NULL == instance)
{
instance =new SingleTon;
}
else
{
cout<<"itis error!"<<endl;
}
returninstance;
}
//main文件
#include<iostream>
#include "SingleTon.h"
using namespace std;
void main()
{
SingleTon *single;
single = SingleTon::Instance();
SingleTon *second;
second = SingleTon::Instance();
}
程序的运行效果如下:
从上述结果中可以看到,当需要产生两个对象的实例化的时候,第二个就会创建失败。
单例模式在很多场合应用非常广泛,比如说任务管理器。同时,理解起来也是属于众多的设计模式中最为简单的一个。
从上述方式中,可以知道,为了确保整个过程中只有一个单例被创建,所以在对象的初始化函数中,就需要对其设置成静态变量,以方便控制。再者,在实例化的过程中,通过对当前对象是否已经被实例化进行判断,从而保证只有一个实例存在。
但是,这其中会出现一个问题,这个问题也是单例模式中最麻烦的一个问题,就是多线程的时候,单例模式很可能会产生失效的现象。
因为,在进行实例化判断的这个短暂时间内,其他线程就再次创建了这样的一个实例,从而导致了单例模式的失败。
上面实现的单例模式是采用较为通用的一种方法---懒汉模式。而通常与懒汉模式想比较而言的还有另外的一种实现方式,称之为饿汉模式。想比较而言,饿汉模式的缺陷就在于没一开始就完成了对象的实例化,而不能够起到延迟实现的效果。
class HungrySingleton{
private:
~Singleton(); //避免被外界delete
Singleton& operator=(constSingleton&);//关闭赋值运算符
static HungrySingletonsingleton=new HungrySingleton();
HungrySingleton(){}
Public:
static HungrySingleton getInstance(){
returnsingleton;
}
上述的问题还是没有解决,关于多线程同步中,往往大家想的比较好的一个方法,就是互斥锁的方法。确实,互斥锁是能够解决多线程下单例模式的一个好方法。
其实还有一种方法,由于实现方式是通过java实现的,而如果通过C++方法实现的话,相对难度会大了一些,再者,直观性显然没有java的明显。因此,就主要对双重校验锁方法进行说明一下。
classSingleton:{
// 其它成员
public:
inline locker(){ pthread_mutex_init(&mutex,NULL);}
inline ~locker(){ pthread_mutex_destroy(&mutex);}
inline void lock(){ pthread_mutex_lock(&mutex);}
inline void unlock(){ pthread_mutex_unlock(&mutex);}
static Singleton * GetInstance(){
if (m_pInstance == NULL){
locker llock;
llock.lock();
if(m_pInstance == NULL){
m_pInstance= new Singleton()
}
Llock.unlock();
}
return m_pInstance;
}
private:
Singleton(){};
~Singleton(); //避免被外界delete
Singleton& operator=(constSingleton&);//关闭赋值运算符
staticSingleton * m_pInstance;
}
可以看到,双重校验锁的实现机制是基于懒汉模式实现基础上进行改造的,多出了
inline locker(){ pthread_mutex_init(&mutex,NULL);}
inline ~locker(){ pthread_mutex_destroy(&mutex);}
inline void lock(){ pthread_mutex_lock(&mutex);}
inline void unlock(){ pthread_mutex_unlock(&mutex);}
这是个函数接口,这是个函数接口就是用于保证在多线程的过程中不会产生失败的现象。
之前有人讲过的一个关于单例模式的课,里面有这个的相关例子,故在此,直接引用这个实例。
同时,在实例化的Instance接口中,添加双重校验锁的一个保证,先对对象进行一个判断,然后将结果锁定之后,再进行判断,之后再完成对象的实例化。当完成了实例化之后,才进行解锁。这样的一个处理过程保证了对象不会在多线程中被二次创建,从而实现了单例的唯一性。