线程安全的单例模式
什么是单例模式
在上一章节中,我们对线程池进行了封装,但是实际上存在一个漏洞。我们一个对象会一次性创建出一批线程出来,那么如果有多个对象并且每个对象都申请线程呢?这不就会造成线程被滥用吗?为了解决这种问题场景,我们设计出了单例模式!
特点
某些类,只应该具有一个对象(实例),就称之为单例。 在很多服务器开发场景中,经常需要让服务器加载很多的数据(上百 G)到内存中。此时往往要用一个单例的类来管理这些数据。
饿汉模式和懒汉模式的单例模式
饿汉?懒汉?这里举个例子方便大家进行理解:
吃完饭,立刻洗碗,这种就是饿汉方式。因为下一顿吃的时候可以立刻拿着碗就能吃饭。
吃完饭,先把碗放下,然后下一顿饭用到这个碗了再洗碗,就是懒汉方式。
饿汉模式实现
只要通过 Singleton 这个包装类来使用 T 对象,则一个进程中只有一个 T 对象的实例。
template <typename T> class Singleton {
static T data;
public:
static T* GetInstance() { return &data; }
};
static 变量将来被编译器编译,加载器加载,静态变量将来会被编译在进程地址空间的哪个区域?
在 C++ 语言上经常可以做类加载和创建类,在系统角度又是什么意思?
data 变量一旦被定义,会在进程的全局数据区进行开辟。在系统角度上不就是编译器编译到全局变量去了,运行时该变量已经被加载了,已经存在了。不使用时变量就已经被开辟出来了(还没吃碗就立即洗了),这叫做进程加载时类对象直接被创建。
可是为什么是单例的呢?
- 全局变量,变量名不能冲突
- 只要是单例,构造拷贝不要创建
懒汉模式实现
// 懒汉模式,线程安全
template <typename T> class Singleton {
volatile static T* inst; // 需要设置 volatile 关键字,否则可能被编译器优化。
static std::mutex lock;
public:
static T* GetInstance() {
if (inst == NULL) {
// 双重判定空指针,降低锁冲突的概率,提高性能。
lock.lock();
(inst == ) {
inst = ();
}
lock.();
}
inst;
}
};









