1、教科书里的单例模式
简单的单例模式实现起来是很简单的:只需将类的构造函数声明为private或者protect防止被外部实例化,内部保存一个private static的类指针保存唯一的实例,实例动作由一个public的类方法代劳,该方法也返回单例的唯一实例。代码实现如下:
class CSignal
{
public:
static CSignal *getInstanc();
protected:
CSignal(){};
private:
static CSignal *m_pInstance;
}
CSignal *CSignal::m_pInstance = NULL;
CSignal *CSiganl::getInstance()
{
if(m_pInstance == NULL)
{
m_pInstance = new CSignal();
}
return m_pInstance;
}
这是单例模式的简单实现,简单易懂,但是这不是一个完美的实现,这个方法不是线程安全的,当有两个线程同时首次调用getInstance方法且同时检测到m_pInstance的值是NULL,这两个线程就会同时构造一个实例m_pInstance,这是个严重的错误,同时,这也不是单例的唯一实现!
2、懒汉与恶汉
单例的实现大体上有两种实现方法:懒汉实现与饿汉实现。
懒汉:一个字就是懒,不到万不得已的时候不会去实例化类,也就是说第一次用到类实例的时候才会去实例化,所以上边的教科书式的写法属于懒汉实现;
饿汉:饿了就要吃东西,而且是饥不择食,所以饿汉实现就是在单例类定义的时候就进行实例化。
两种实现模式的特点与选择:
由于要进行线程同步,所以访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。在访问量较小的时候,采用懒汉实现,这是以时间换空间。
3、线程安全的懒汉实现
线程不安全,最直观的方法就是枷锁。
方法1:加锁的经典懒汉实现:
class CSignal
{
public:
static pthread_mutex_t mutex;
static CSignal *getInstanc();
protected:
CSignal()
{
pthread_mutex_init(&mutex);
};
private:
static CSignal *m_pInstance;
}
CSignal*CSignal::m_pInstance=NULL;
CSignal *CSiganl::getInstance()
{
if(m_pInstance == NULL)
{
pthread_mutex_lock(&mutex);
if(m_pInstance == NULL)
{
m_pInstance = new CSignal();
}
pthread_mutex_unlock(&mutex)
}
return m_pInstance;
}
这个方法实现起来也很容易,在instance函数里定义一个静态实例,也可保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐使用这种方法,真的很简单,也很好用。实现代码如下:
class CSignal
{
public:
static pthread_mutex_init(&mutex);
static CSignal *getInstance();
int a;
protected:
CSignal()
{
pthread_mutex_init(&mutex);
};
}
pthread_mutex_t signal::mutex;
CSignal *CSiganl::getInstance()
{
ptread_mutex_lock(&mutex);
static CSignal m_pInstance;
ptread_mutex_unlock(&mutex);
return &m_pInstance;
}
4、饿汉实现
饿汉实现是线程安全的,不用加锁,这种就是用空间换取时间,多线程可以使用这种实现。代码如下:
class CSignal
{
public:
static CSignal *getInstance();
protected:
CSignal(){};
private:
static CSignal *m_pInstance;
}
CSignal *CSignal::m_pInstance = new CSignal;
CSignal *CSiganl::getInstance()
{
return m_pInstance;
}