我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法返回单例类唯一的实例。
class Singleton
{
private:
Singleton() //私有构造函数
{}
static Singleton* instance;//private static的类指针保存唯一的实例
public:
static Singleton* GetInstance();//静态成员方法获取实例
};
Singleton* Singleton::instance = NULL;
Singleton* Singleton::GetInstance()
{
if (instance == NULL)
instance = new Singleton();
return instance;
}
现在如果有两个线程同时调用GetInstance方法且同时检测到instance 是NULL值,则两个线程会同时构造一个实例给instance ,这是非常错误的操作。
现在来介绍一下安全的操作方法:
1、懒汉模式:典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。第一次用到类实例的时候才会去实例化。 其实上面的实例就属于懒汉模式,但是它是线程不安全的。
线程安全的懒汉模式(加锁):
class Singleton
{
private:
Singleton()
{}
static Singleton* instance ;
public:
static pthread_mutex_t mutex;
static Singleton* GetInitance();
};
pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
Singleton* Singleton::instance = NULL;
Singleton* Singleton::GetInitance()
{
if (instance == NULL)
{
pthread_mutex_lock(&mutex);//加锁
if (instance == NULL)
instance = new Singleton();
pthread_mutex_unlock(&mutex);//释放锁
}
return instance ;
}
内部静态变量的懒汉模式:
class Singleton
{
private:
Singleton()
{}
public:
static pthread_mutex_t mutex;
static Singleton* GetInitance();
};
pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
Singleton* Singleton::GetInitance()
{
pthread_mutex_lock(&mutex);//加锁
static Singleton obj;//定义一个静态实例
pthread_mutex_unlock(&mutex);//释放锁
}
return &obj;
}
2、饿汉模式:典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。单例类定义的时候就进行实例化。
饿汉模式本身就是线程安全的:
class Singleton
{
private:
Singleton(){}
static Singleton* instance;
public:
static Singleton* GetInstance();
};
Singleton* Singleton::instance = new Singleton();
Singleton* Singleton::GetInstance()
{
return instance;
}
两种模式的适用场景:
饿汉模式:访问量比较大,或者可能访问的线程比较多时,由于要进行线程同步,采用饿汉模式,可以实现更好的性能。这是以空间换时间。
懒汉模式:在访问量较小时,采用懒汉实现。这是以时间换空间。