设计模式之单例模式(Singleton)

概念
单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
动机
在软件系统中,经常有这样一些特殊的类,必须保证他们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
如何绕过常规的构造器,提供一种机制来确保一个类只有一个实例?
保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
在这里插入图片描述
为什么使用单例模式

在应用系统开发中,我们常常有以下需求:

  • 在多个线程之间,比如初始化一次socket资源;比如servlet环境,共享同一个资源或者操作同一个对象
  • 在整个程序空间使用全局变量,共享资源
  • 大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。
    因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了

实现单例步骤常用步骤

Singleton 模式其实是对全局静态变量的一个取代策略,上面提到的 Singleton 模式的两个作用在 C++中是通过如下的机制实现的:1)仅有一个实例,提供一个 类的静态成员变量,大家知道类的静态成员变量对于一个类的所有对象而言是惟 一的 2)提供一个访问它的全局访问点,也就是提供对应的访问这个静态成员变 量的静态成员函数,对类的所有对象而言也是惟一的.在C++中,可以直接使用类 域进行访问而不必初始化一个类的对象.

对于多线程下的单例模式,需要使用双检查锁,双检查锁的正确实现必须要注意内存的reorder
代码:

class Singleton{
private:
   Singleton();
   Singleton(const Singleton& other);
public:
   static Singleton* getInstance();
   static Singleton* m_instance;
}; 
Singleton* Singleton::m_instance=nullptr;
//线程非安全版本
Singleton* Singleton::getInstance() {   
if (m_instance == nullptr) {       
	m_instance = new Singleton();
    }   
return m_instance;
}
//线程安全版本,但锁的代价过高
Singleton* Singleton::getInstance() {   
Lock lock;   
if (m_instance == nullptr) {       
	m_instance = new Singleton();
    }   
return m_instance;
}
//双检查锁,但由于内存读写reorder不安全
Singleton* Singleton::getInstance() {       
if(m_instance==nullptr){       
	Lock lock;
       if (m_instance == nullptr) {
           m_instance = new Singleton();
       }
    }
   return m_instance;
}
//C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*>
Singleton::m_instance;
std::mutex Singleton::m_mutex;
 
Singleton* Singleton::getInstance() {
   Singleton* tmp = m_instance.load(std::memory_order_relaxed);
   std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
   if (tmp == nullptr) {
       std::lock_guard<std::mutex> lock(m_mutex);
        tmp =m_instance.load(std::memory_order_relaxed);
       if (tmp == nullptr) {
           tmp = new Singleton;
           std::atomic_thread_fence(std::memory_order_release);//释放内存fence
           m_instance.store(tmp, std::memory_order_relaxed);
       }
    }
   return tmp;
}

懒汉式的Double-Check是一个经典问题!为什么需要2次检查 “if(pInstance == NULL)”

场景:假设有线程1、线程2、线程3,同时资源竞争。

//1)第1个、2个、3个线程执行第一个检查,都有可能进入黄色区域(临界区)

//2)若第1个线程进入到临界区,第2个、第3个线程需要等待

//3)第1个线程执行完毕,cs.unlock()后,第2个、第3个线程要竞争执行临界区代码。

//4)假若第2个线程进入临界区,此时第2个线程需要再次判断
if(pInstance == NULL)”,若第一个线程已经创建实例;第2个线程就不需要再次创建了。保证了单例;

//5)同样道理,若第2个线程,cs.unlock()后,第3个线程会竞争执行临界区代码;此时第3个线程需要再次判断 if(pInstance == NULL)。通过检查发现实例已经new出来,就不需要再次创建;保证了单例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值