线程安全
单例模式在单线程中,当然是安全的。但是如果在多线程中,由于并行判断,可能会导致创建多个实例。那么如何保证在多线程中单例还是只有一个实例呢?
常见的三种方式:
局部静态变量
原理和饿汉模式相似,利用static只会初始化一次的特性,并且在第一次调用的情况下才会被初始化。推荐使用
class Singleton {
private:
Singleton() { };
public:
static Singleton* getInstance() {
static Singleton *instance = new Singleton();
return instance;
}
};
饿汉模式
原理:利用static,在程序编译的时候就调用构造函数实现单例,这样做的优点是保证线程安全,但是缺点就是无论后续是否用到,在编译的时候就会创建,会导致启动性能降低。
实现方法:
class Singleton_Hungry {
public:
static Singleton_Hungry* getInstance() {
return singleton;
}
private:
Singleton_Hungry() {
cout << "Hungry creat." << endl;
}
static Singleton_Hungry* singleton;
};
Singleton_Hungry* Singleton_Hungry::singleton = new Singleton_Hungry();
懒汉模式
原理:利用线程锁,在获取实例的时候判断实例加上线程锁,保证判断的条件只允许一个线程操作。利用锁也可以保证单例只有一个实例。
实现方法:
#include <mutex>
std::mutex mu;
class Singleton_Lazy {
public:
static Singleton_Lazy* getInstance() {
if (singleton == NULL) {
mu.lock();//打开锁
if (singleton == NULL) {
singleton = new Singleton_Lazy();
}
mu.unlock();//关闭锁
}
return singleton;
}
private:
Singleton_Lazy() {
cout << "Lazy creat." << endl;
}
static Singleton_Lazy* singleton;
};
Singleton_Lazy* Singleton_Lazy::singleton = NULL;
实践验证
在linux系统上通过命令行g++ single.cpp --std=c++11 -lpthread
编译
#include <iostream>
#include <mutex>
#include <thread>
#include <unistd.h>
using namespace std;
mutex mu;
class Singleton_Hungry {
public:
static Singleton_Hungry* getInstance() {
return singleton;
}
private:
Singleton_Hungry() {
cout << "Hungry creat." << endl;
}
static Singleton_Hungry* singleton;
};
Singleton_Hungry* Singleton_Hungry::singleton = new Singleton_Hungry();
class Singleton_Lazy {
private:
Singleton_Lazy() {
cout << "Lazy creat." << endl;
}
static Singleton_Lazy* singleton;
public:
static Singleton_Lazy* getInstance() {
if (singleton == NULL) {
//mu.lock();//打开锁
if (singleton == NULL) {
singleton = new Singleton_Lazy();
}
//mu.unlock();//关闭锁
}
return singleton;
}
};
Singleton_Lazy* Singleton_Lazy::singleton = NULL;
void thr(int t) {
cout << t << " pthread id: " << pthread_self() << endl;
for(int i = 0; i < 3; i++) {
Singleton_Lazy *lazy = Singleton_Lazy::getInstance();
Singleton_Hungry *hungry = Singleton_Hungry::getInstance();
cout << t << " lazy addr:" << lazy << endl;
cout << t << " hungry addr:" << hungry << endl;
}
}
int main() {
cout<<"main process id: "<<getpid()<<endl;
cout<<"main pthread id:"<< pthread_self()<<endl;
thread thread1(thr, 1);
thread thread2(thr, 2);
thread1.join();
thread2.join();
return 0;
}
结果分析
结果和预想一致,饿汉模式在程序编译阶段调用构造函数,懒汉模式在调用的时候创建,如果不加线程锁会导致创建多个实例。