单例模式汇总篇续
来源:互联网 发布:淘宝装修设计师模块 编辑:程序博客网 时间:2024/06/03 22:14
又是一个美好的下午,斜斜的阳光洒在我的键盘上,好吧,其实是个无聊的下午,同学在讲台上讲他的学生管理系统,还是做点儿有意思的事儿吧。
书接上篇,我们已经写了好几种单例模式,现在我们来说说,上面几种单例模式存在的问题。
带GC的单例模式:
看这段代码:
static Singleton * GetInstance() { if(m_instance == NULL) m_instance = new Singleton(); return m_instance; }
当有多个线程同时调用GetInstance()时,由于线程调度,当线程A执行完if语句后,可能时间片用完,线程A由运行状态->就绪状态,此时刚好线程B也要执行GetInstance(),在线程B时间片用完后,线程A获得时间片,继续执行m_instance = new Singleton(); 所以这种延迟加载的模式存在线程安全的问题。那怎么解决这个问题呢?
我们可以采用双检锁模式(double checked locking pattern),如下:
static Singleton * GetInstance() { if(m_instance==NULL) { LOCK();//这个锁需要自己实现 if(m_instance == NULL) m_instance = new Singleton(); UNLOCK();// } return m_instance; }
逻辑上,只有当m_instance为空时,我们加锁,构建实例;当m_instance不为空时,直接返回m_instance就行了,所以锁要加在判断语句后面。另外如果我们把锁加在判断语句之前,那么在m_instance非空时,也会频繁的加锁解锁,浪费性能。还有一个问题为什么使用双检锁?下面的代码会存在什么问题?
static Singleton * GetInstance() { if(m_instance==NULL) { LOCK();//这个锁需要自己实现 m_instance = new Singleton(); UNLOCK();// } return m_instance; }
其实这样写也是会存在线程安全的问题,因为有可能会有多个线程同时通过if(m_instance==NULL),所以我们需要多一层检查。采用智能指针的线程安全方案于此同理。
Meyers Singleton:
Meyers大师采用的是局部静态对象,局部静态对象的初始化在C++0X与C++11有很大的差异。
首先我们来看一下在C++0X中:
局部静态变量在编译时,编译器的实现一般是在初始化语句之前设置一个局部静态变量的标识来判断是否已经初始化,运行的时候每次进行判断,如果需要初始化则执行初始化操作,否则不执行。这个过程本身不是线程安全的. 根据这段描述,我们来写一段伪代码:
static Singleton& GetInstance(){ static bool isInit = false; static Singleton m_instance;//(未初始化的m_instance) if(isInit==false) { m_instance = *(new Singleton());//new 返回的是指针,需要解引用 } return m_instance;}
在上面的伪代码中,我们一目了然,这种模式也是非线程安全的。解决办法可以采用上面提到的双检锁。
再来看看在C++11中:
C++11标准规定了局部静态变量初始化需要保证线程安全,具体说明如下:If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialisation。–如果指令逻辑进入一个未被初始化的声明变量,所有并发执行应当等待完成该变量完成初始化。所以在C++11中,Meyers Singleton不仅是代码量最少,而且是线程安全的。
饿汉模式:
上面几种都是懒汉模式,延迟加载,下面我们来看饿汉模式:
class Singleton{public: static std::shared_ptr<Singleton> GetInstance() { //if(m_instance == NULL) // m_instance = std::shared_ptr<Singleton>(new Singleton); return m_instance; }public: ~Singleton()//必须是public 由智能指针调用 { std::cout<<"destructor has been called\n"; }private: static std::shared_ptr<Singleton> m_instance; Singleton() { std::cout<<"constructor has been called\n"; }};std::shared_ptr<Singleton> Singleton::m_instance = std::shared_ptr<Singleton>(new Singleton);
这里直接使用了智能指针,实现自动回收。
类的静态成员变量在main函数之前进行初始化,所以当调用GetInstance()时,单例实例已经存在,所以不会有线程安全问题,饿汉模式是线程安全的。
饿汉模式好像很完美啊,其实有一个比较傻x的隐患,例如有两个单例类,在单例A的构造函数里调用单例B的方法,
伪代码:
A(){ B::GetInstance()->do();}
饿汉模式的对象是在类加载的时候构建的,如果A执行构造函数的时候,B还没有加载,那么就会出错。解决办法是当我们使用饿汉模式时要避免这种写法。。。
到此闲话单例模式说完~~~,欢迎补充~
ps:刚刚上讲台,台下一脸懵逼,我也是呵呵哒了~~~
- 单例模式汇总篇续
- 单例模式汇总篇
- 单例模式汇总
- 单例模式汇总
- 单例模式(汇总)
- 单例模式,工厂模式,代理模式汇总
- (JAVA)static\访问权限\单例模式 大汇总
- 单例模式——最全写法汇总
- 单例模式续
- 第八篇:单例模式
- 设计模式一:单例模式(续)
- java设计模式---单例模式篇
- 单例、单例模式
- 单例模式-多线程单例模式
- 单件模式(单例模式)
- java设计模式-单例模式讨论篇:单例模式与垃圾回收
- 设计模式------单例模式
- 设计模式------单例模式
- java 父类引用指向子类对象---动态绑定之易错点详解
- 为程序界面添加滑动条
- ScrollView嵌套WebView冲突解决方法
- vs2013如何快速批量注释
- 安装好 .NET 4 后还是找不到设定网站站台的 ASP.NET 页签的 ASP.NET 4.0 的选项
- 单例模式汇总篇续
- Jmeter:正则表达式提取器上一个http请求报文内容作为下一个请求的参数
- Ural1521-War Games 2
- 图像的卷积 以及opencv基本操作
- Android静态安全检测 -> Zip文件目录遍历漏洞
- tomcat 集群
- Struts2 JSP的标准动作<jsp:forward>无法访问Action
- 【转】取模(mod)与取余(rem)的区别——Matlab学习笔记
- sqlyog注册码激活