单例模式的知识点
- 单例模式的为什么这么写?
- 单例模式中的构造函数为什么是private?
- instance为什么是static?
- 如何获取单例?
- 什么是懒汉模式?
- 懒汉模式存在的问题?如何解决?
- 饿汉模式?
首先,构造函数是private是要保证单例,如果是public自然是可以初始化多个对象的;
其次,instance是static的原因是成员表示类的实例而不是属于类的各个对象;
从而,private+static保证了我们获取对象的唯一性;
再次,还需要一个获取这个单例的入口,就是getInstance,从这个接口里面返回单例。
// 线程不安全的单例模式(懒汉模式)
class SingleClass{
public:
static SingleClass* getInstance(){
if(instance == nullptr){
instance = new SingleClass();
cout<<"创建了一个单例模式对象 @" << instance <<endl;
}
return instance;
}
private:
static SingleClass* instance;
SingleClass(){};
};
// 初始化静态成员变量
SingleClass* SingleClass::instance = nullptr;
int main()
{
SingleClass* s1 = SingleClass::getInstance();
SingleClass* s2 = SingleClass::getInstance();
return 0;
}
//在这种“懒汉式”单例模式中,存在的线程不安全问题主要是当多个线程同时检测到instance == nullptr时,它们都可能尝试创建该对象实例。结果,你可能会结束得到多个SingleClass对象实例,违反了单例模式的设计原则。
// 线程安全的单例模式(懒汉模式)
public:
static SingleClass* getInstance(){
if(instance == nullptr){
// 创建实例之前加锁
mtx.lock();
if(instance == nullptr)
{
// 再判断一次,确保不会因为加锁期间多个线程同时进入
instance = new SingleClass();
}
// 解锁
mtx.unlock();
}
return instance;
}
private:
static SingleClass* instance;
SingleClass(){};
static mutex mtx;
};
// 初始化静态成员变量
SingleClass* SingleClass::instance = nullptr;
mutex SingleClass::mtx;
int main()
{
SingleClass* s1 = SingleClass::getInstance();
SingleClass* s2 = SingleClass::getInstance();
return 0;
}
// 1 两个if的原因:如果没有第二个if则当第一个线程释放锁时,下一个线程加锁则又会创建一个新的单例对象,违背单例的原则
// 2 为什么不在第一个if上加锁,若在第一个if之前加锁每次调用getInstance()函数时,都会进行锁操作,即使单例对象已经创建了。锁操作相对来说是昂贵的,特别是在高并发环境下。高并发环境下很多线程同时调用getInstance(),会对锁产生竞争,不得不等待锁的释放,从而逐渐退化为单线程模式
最后是饿汉模式,与懒汉模式不同的是类一加载就实例化对象,即在执行main函数之前就有了对象,这样在getInstance函数里面不用再去new instance,直接return instance
public:
static SingleClass* getInstance(){
return instance;
}
private:
static SingleClass* instance;
SingleClass(){};
static mutex mtx;
};
SingleClass* SingleClass::instance = new SingleClass();