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;
}

 

posted @ 2023-11-12 07:35  宇宙之母蔡依林  阅读(73)  评论(0)    收藏  举报