单例模式是一种非常经典的设计模式,一个类在内存中只能实例化一个对象,资源在内存中只能有一份,提供一个统一的接口进行访问。
要实现单例模式可以采用两种方式:饿汉方式、懒汉方式。
- 饿汉方式:在程序的初始化阶段完成资源的申请加载初始化以及对象的实例化,可以将其看作是一个吃完饭立马洗碗的饿汉,因为这样下一次吃饭时就可以直接端起碗吃饭,这种方式的思想是以空间换时间。例如市面上的大型手游王者荣耀就采用的是这种思想,在游戏开始之前初始化完所有资源,在游戏进行中就避免了再去加载资源,保持游戏的流畅度。
用饿汉方式来实现单例模式:通过 Singleton 这个包装类来使用 T 对象, 则一个进程中只有一个 T 对象的实例。
template <typename T>
class Singleton {
static T data;
public: static T* GetInstance() {
return &data;
}
};
- 懒汉方式:资源在使用时才申请加载初始化,可以将其看作一个吃完饭不洗碗的懒汉,在下一次吃饭之前再去洗碗,这是延迟加载的思想。例如支付宝就采用的是这种思想,支付宝中各类模块很多,但为了保证打开的速度够快,所以在打开时只加载使用模块的资源,并不去初始化别的模块资源,优化服务器的启动速度。
用懒汉方式来实现单例模式:要注意双重 if 判定, 避免不必要的锁竞争,还要使用volatile关键字防止过度优化。
template <typename T>
class Singleton {
volatile static T* inst; // 设置 volatile 关键字防止被编译器优化
static std::mutex lock;
public:
static T* GetInstance() {
if (inst == NULL) { // 双重判定空指针, 降低锁冲突的概率, 提高性能
lock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次 new
if (inst == NULL) {
inst = new T();
}
lock.unlock();
}
return inst;
}
};