2023蚂蚁金服/猿辅导实习手撕题——单例模式(double check)
单例模式的要点:
1、构造函数私有化,保证不能从外部新建对象;
2、懒汉(要用到时再检查是否有初始化)和饱汉(先初始化)
初版代码(C++):
#include <iostream> class Instance{ private: // 最重要的事情:构造函数私有化 Instance(){} // 静态指针:类的多个对象都只能共享一份 static Instance *instance; public: // 静态方法:可以通过类名访问这个方法 static Instance *getInstance() { if (instance == nullptr) { instance = new Instance(); } return instance; } }; // 需要在类外初始化静态成员 Instance* Instance::instance = nullptr;
下面是改进版。
double check的意思是针对多线程环境下,可能会出现创建多次实例的情况,所以要2次检查是否已经实例化。
如果没有double check的话就会是这样:
#include <iostream> #include <mutex> class Instance{ private: Instance(){} static Instance *instance; static std::mutex mutex; public: static Instance *getInstance() { mutex.lock(); if (instance == nullptr) { instance = new Instance(); } mutex.unlock(); return instance; } }; Instance* Instance::instance = nullptr;
会出现什么问题呢?不会有问题,但是每个线程要检查instance是否被创建过都要获取一次mutex,这样会导致效率低下,从安全层面讲没任何毛病。
另一种不使用double check的情况是,预先判断instance是否为空,为空了再获取mutex创建instance,就是将上面代码里的mutex的lock()和unlock()放到if内部。
这样会导致的问题是:A线程在获取mutex的过程中,已经有B线程创建好instance了,但是A线程已经进入到if内,所以还会再创建一次instance。
所以既提高效率又保证安全就是double check方式,代码如下:
#include <iostream> #include <mutex> #include <string> #include <vector> #include <thread> class Instance{ private: // 最重要的事情:构造函数私有化 Instance(){} // 静态指针:类的多个对象都只能共享一份 static Instance *instance; static std::mutex mutex; public: static Instance *getInstance() { if (instance == nullptr) { mutex.lock(); if (instance == nullptr) { std::cout<<"create the only instance\n"; instance = new Instance(); } mutex.unlock(); } return instance; } }; // 需要在类外初始化静态成员 Instance* Instance::instance = nullptr; std::mutex Instance::mutex; auto main() -> int { std::vector<std::thread> jobs; for (int i = 0; i < 10000; i++) { jobs.emplace_back([]{ Instance::getInstance(); }); } for (int i = 0; i < 10000; i++) { jobs[i].join(); } return 0; }

浙公网安备 33010602011771号