设计模式之单例模式

1.什么是单例模式

单例模式是指在整个系统生命周期内,保证一个类只能产生一个实例,确保该类的唯一性。

2.为什么需要单例模式

两个原因:

节省资源。一个类只有一个实例,不存在多份实例,节省资源。
方便控制。在一些操作公共资源的场景时,避免了多个对象引起的复杂操作。
但是在实现单例模式时,需要考虑到线程安全的问题。

3.线程安全

什么是线程安全?
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

如何保证线程安全?
给共享的资源加把锁,保证每个资源变量每时每刻至多被一个线程占用。
让线程也拥有资源,不用去共享进程中的资源。如:使用threadlocal可以为每个线程维护一个私有的本地变量。

4.单例模式分类

单例模式可以分为 懒汉式 和 饿汉式 ,两者之间的区别在于创建实例的时间不同。

懒汉式
系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。这种方式要考虑线程安全。

饿汉式
系统一运行,就初始化创建实例,当需要时,直接调用即可。这种方式本身就线程安全,没有多线程的线程安全问题。

5.单例类的特点

构造函数和析构函数为私有类型,目的是禁止外部构造和析构。
拷贝构造函数和赋值构造函数是私有类型,目的是禁止外部拷贝和赋值,确保实例的唯一性。
类中有一个获取实例的静态方法,可以全局访问。

  1. 单例模式实现
    单例的经典实现方式是「静态局部变量的懒汉单例」,推荐使用这种方式。

懒汉模式 代码示例:

/*C++ 11  之前*/
class Singleton
{
private:
    static Singleton* m_SingleInstance;
private:
    Singleton() { };
    ~Singleton() { };
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

private:
    class Deletor {
    public:
        ~Deletor() {
            if(Singleton::instance != NULL)
                delete Singleton::instance;
        }
    };
    static Deletor deletor;
public:
    static Singleton* getInstance() 
    {
    //  这里使用了两个 if 判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,
    //  避免每次调用 GetInstance的方法都加锁,锁的开销毕竟还是有点大的。
        if (m_SingleInstance == nullptr) 
        {
            std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
            if (m_SingleInstance == nullptr)
            {
                volatile auto temp = new (std::nothrow) SingleInstance();
                m_SingleInstance = temp;
            }
        }

    }
    return instance;
}
};

// init static member
Singleton* Singleton::m_SingleInstance = NULL;
/*---------------------------------------------------------------------------------*/

/*C++ 11 版本下*/
class Singleton
{
private:
    Singleton() { };
    ~Singleton() { };
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
public:
    static Singleton& getInstance() 
        {
            static Singleton instance;  //C++11规定了local static在多线程条件下的初始化行为,要求编译器保证了内部静态变量的线程安全性。在C++11标准下,《Effective C++》提出了一种更优雅的单例模式实现,使用函数内的 local static 对象。
            return instance;
        }
};

饥汉模式 代码示例:

class Singleton
{
private:
    static Singleton instance;    //它是个实例不是指针
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
public:
    static Singleton& getInstance() {
        return instance;
    }
}

// initialize defaultly
Singleton Singleton::instance;