定义:
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
class Singleton{
private:
Singleton(){}
Singleton(const Singleton& f){}//构造函数和拷贝构造函数设为私有
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
Singleton* Singleton::m_instance=nullptr;//静态成员函数或者变量类外初始化
//线程非安全版本
Singleton* Singleton::getInstance()
{
if(m_instance==nullptr)//线程a执行在此 时间片用完 此时m_instance=nullptr 换线程b执行在此 生成实例化对象 然后a继续执行 因此最后会出现多个实例化对象
{
m_instance=new Singleton();
}
return m_instance;
}
//线程安全版本,直接加锁
#include <mutex>
static mutex Lock1;//定义锁
Singleton* Singleton::getInstance()
{
Lock1.lock();
//线程a抢到锁 时间片用完 换线程b执行 执行到此成为阻塞态 而b只需要读取当前的m_instance即可,
//因此读操作最好不加锁。(高并发情况存在设计缺陷)
if(m_instance==nullptr)
{
m_instance=new Singleton();
}
Lock1.unlock();
return m_instance;
}
//线程安全版本,双检测锁
#include <mutex>
static mutex Lock1;//定义锁
Singleton* Singleton::getInstance()
{
if(m_instance==nullptr)//检测1
{
Lock1.lock();
if(m_instance==nullptr)//检测2
m_instance=new Singleton();
}
Lock1.unlock();
return m_instance;
}
/*
Singleton* Singleton::getInstance()
{
if(m_instance==nullptr)
{
Lock1.lock();
m_instance=new Singleton();
}
Lock1.unlock();
return m_instance;
}
如图所示 如果上图代码改成单检测锁,则a线程执行进来加锁 时间片用完 b也进来等a释放锁 a创建完对象释放锁 b拿到锁后也会继续执行创建对象 然后释放锁 此时加锁无任何意义
*/
/*然而双检测锁 存在内存读写reorder不安全
执行m_instance=new Singleton();
假定顺序:1.cpu先分配内存 2.再调用构造器 3.返回创建对象的地址值
然而实际上前面的三个步骤不一定顺序执行,编译器对代码会进行优化处理
因此 如果一个线程a 先分配内存 返回创建对象的地址值 构造器未调用
而进程b执行第一个检测时 发现指针不为空返回 地址值 得到了对象 但是该对象由于未调用构造器
因此并不能使用 解决方法volatile关键字
*/