1、单例模式
一个类只有一个实例,提供一个全局访问点来访问这个实例。
分为懒汉模式和饿汉模式:
- 懒汉模式就是 只有用到这个实例才会初始化对象并返回 (调用了对外的接口才实例化对象)
- 饿汉模式就是 不管用不用得到,都先构造出来,先初始化
双重锁的懒汉模式:
cpp
#include <mutex>
class SingeLazy {
private:
SingeLazy() {}
SingeLazy(const SingleLazy& ) = delete;
SingeLazy& operator= (const SingleLazy& ) = delete;
static SingleLazy* instance;
static mutex i_mutex;
public:
static SingleLazy* getInstance() {
if (instance == nullptr) {
mutex.lock();
if (instance == nullptr) {
instance = new SingleLazy();
}
mutex.unlock();
}
return instance;
}
};
SingleLazy* SingleLazy::instance = nullptr;
mutex SingleLazy::i_mutex; // 类外初始化
双重锁机制(Double-Checked Locking)用于确保单例对象只被创建一次,并且提高线程安全性和性能。
为什么要进行第二次检查 if (instance == nullptr)
?
当第一个线程获得锁并创建单例对象时,其他等待的线程可能已经通过了第一次检查 if (instance == nullptr)
,并准备进入加锁阶段。如果没有第二次检查,当第一个线程释放锁之后,第二个线程会获得锁并尝试再次创建对象。
场景描述:
- 线程A进入 getInstance(),通过了第一次检查,发现 instance == nullptr,然后加锁并创建实例。
- 线程B几乎同时进入 getInstance(),也通过了第一次检查,因为当时 instance 还没被创建,但它被阻塞在锁等待状态。
- 当线程A创建了实例并释放锁,线程B获得锁,并进入锁定区域。如果没有第二次检查,线程B会认为 instance 仍然是
nullptr,并再次尝试创建实例。
因此,第二次检查的作用是防止在第一个线程创建实例后,其他线程重复创建实例。