Singleton 单件模式及其变体 Double-Checked Locking 双重检查锁模式
2009-10-17 09:50
393 查看
Singleton单件模式,及其变体Double-CheckedLocking双重检查锁模式,都可以用于确保某个类只有一个对象实例化.两个模式的区别在于:Singleton模式用于单线程程序中,而Double-CheckedLocking模式用于多线程应用程序.
Singleton模式的意图是:保证一个类仅有一个实例,并提供一个访问它的全局访问点.
工作原理:用一个特殊方法来实例化所需的对象,其中最关键的就是这个特殊的方法:1.调用这个方法时,检查对象是否已经实例化.如果已经实例化,该方法仅返回对象的一个引用.如果尚未实例化,该方法实例化该对象并返回对此新实例的一个引用.2.为了确保这是实例化此类型对象的惟一方法,需要将这个类的构造函数定义为保护或者私有的.
Singleton模式的本质在于,应用程序中的每个对象都使用Singleton类的同一实例,而不用必须负责将该实例四处传递给所有要使用它的对象.在一个协作对象对此实例所做的修改需要为另一个协作对象所见时,这一点尤为重要.
Singleton模式的通用结构如下图:
Singleton的关键特征:
意图:希望对象只有一个实例,但没有控制对象实例化的全局对象.还希望确保所有实体使用该对象相同的实例,而无需将引用传给它们.
问题:几个不同的客户对象需要引用同一对象,而且希望确保这种类型的对象数目不超过一个.
解决方案:保证一个实例.
参与者与协作者:Client对象只能通过getInstance方法创建Singleton实例.
效果:Client对象无需操心是否已经存在Singleton实例.这是由Singleton自己控制的.
实现:1.添加一个类的私有静态成员变量,引用所需的对象,初值为NULL.2.添加一个公有静态方法,它在成员变量的值为NULL时实例化这个类(并设置成员变量的值).然后返回该成员变量的值.3.将构造函数设置为保护或私有,从而防止任何人直接实例化这个类,绕过静态构造函数机制.
//实现示例:
注:真正有用的单件对象很少出现,一般单件应该用于代替全局变量.
////
Double-CheckedLockingPattern(DBLP):双重锁模式.DBLP模式仅适用于多线程应用程序.在多线程模式中可能会出现一个问题:假设对getInstance()方法的两个调用几乎同时发生,这种情况可能非常糟糕.
因为C++本身不支持多线程,没有多线程同步的关键词,C++中多线程是用库实现的.
//典型的代码格式如下所示:
一直也没有使用过多线程,不知上面的方法具体情况会不会有问题.
看到一个使用比较完整的实现,贴出来,里面我进行了一些语法上的修改,因为原代码编译通不过,VS2008
Java代码,注意,这个是有问题的.正确的在后面:
对Java不太了解,只把代码贴出来,以防以后参考.上面的方法是有问题的,因为Java编译器本身的优化工作会在构造方法实例化对象之前从构造方法返回指向该对象的引用.因此,在USTax对象真正完全构造之前.doSync就可能完成了.这会带来问题.而且编译器会"注意到""实例"成员"没有办法"在两个if语句之间改变状态,所以会优化掉第二个.可以如下解决这一问题:利用类装载程序:
因为内部类(Instance)将只被装载一次,所以只会创建一个USTax对象.
Singleton模式的意图是:保证一个类仅有一个实例,并提供一个访问它的全局访问点.
工作原理:用一个特殊方法来实例化所需的对象,其中最关键的就是这个特殊的方法:1.调用这个方法时,检查对象是否已经实例化.如果已经实例化,该方法仅返回对象的一个引用.如果尚未实例化,该方法实例化该对象并返回对此新实例的一个引用.2.为了确保这是实例化此类型对象的惟一方法,需要将这个类的构造函数定义为保护或者私有的.
Singleton模式的本质在于,应用程序中的每个对象都使用Singleton类的同一实例,而不用必须负责将该实例四处传递给所有要使用它的对象.在一个协作对象对此实例所做的修改需要为另一个协作对象所见时,这一点尤为重要.
Singleton模式的通用结构如下图:
Singleton的关键特征:
意图:希望对象只有一个实例,但没有控制对象实例化的全局对象.还希望确保所有实体使用该对象相同的实例,而无需将引用传给它们.
问题:几个不同的客户对象需要引用同一对象,而且希望确保这种类型的对象数目不超过一个.
解决方案:保证一个实例.
参与者与协作者:Client对象只能通过getInstance方法创建Singleton实例.
效果:Client对象无需操心是否已经存在Singleton实例.这是由Singleton自己控制的.
实现:1.添加一个类的私有静态成员变量,引用所需的对象,初值为NULL.2.添加一个公有静态方法,它在成员变量的值为NULL时实例化这个类(并设置成员变量的值).然后返回该成员变量的值.3.将构造函数设置为保护或私有,从而防止任何人直接实例化这个类,绕过静态构造函数机制.
//实现示例:
1://Singleton.h
2:#pragmaonce
3:
4:classSingleton
5:{
6:private:
7://私有静态成员变量,引用所需的对象
8:staticSingletons;
9:inti;//数据
10:
11:Singleton(intx);
12:
13://将赋值操作符及拷贝构造函数都设置成私有的
14://且故意不实现,因为它们更本不会被调用
15://声明为私有的,以防止任何此类复制的动作产生
16:Singleton&operator=(Singleton&);
17:Singleton(constSingleton&);
18:
19:public:
20://静态方法,创建Singleton实例的惟一方法
21:staticSingleton&getInstance();
22://存取数据操作
23:intgetSingletonData();
24:voidsetSingletonData(intx);
25:};
1://Singleton.cpp
2:#include"Singleton.h"
3:#include
4:
5://静态成员数据初始化
6:SingletonSingleton::s(10);
7:
8:Singleton::Singleton(intx):i(x)
9:{
10:std::cout<<"Singleton()..."<<std::endl;
11:}
12:
13:Singleton&Singleton::getInstance()
14:{
15:std::cout<<"TheonlywaytoinstanceSingleton!"<<std::endl;
16:returns;
17:}
18:intSingleton::getSingletonData()
19:{
20:std::cout<<"getSingletonData()i="<<i<<std::endl;
21:returni;
22:}
23:voidSingleton::setSingletonData(intx)
24:{
25:std::cout<<"setSingletonData()i="<<x<<std::endl;
26:i=x;
27:}
1://test.cpp
2:
3:#include"Singleton.h"
4:#include
5:
6:intmain()
7:{
8:Singleton&s=Singleton::getInstance();
9:std::cout<<"Singleton::i="<<s.getSingletonData()<<std::endl;
10:
11:Singleton&s2=Singleton::getInstance();
12:s2.setSingletonData(20);
13:std::cout<<"NowSingleton::i="<<s.getSingletonData()<<std::endl;
14:
15:returnEXIT_SUCCESS;
16:}
注:真正有用的单件对象很少出现,一般单件应该用于代替全局变量.
////
Double-CheckedLockingPattern(DBLP):双重锁模式.DBLP模式仅适用于多线程应用程序.在多线程模式中可能会出现一个问题:假设对getInstance()方法的两个调用几乎同时发生,这种情况可能非常糟糕.
因为C++本身不支持多线程,没有多线程同步的关键词,C++中多线程是用库实现的.
//典型的代码格式如下所示:
1://如下典型的格式来自DouglasC.Schmidt
2://和TimHarrison的论文Double-CheckedLocking
3:classSingleton
4:{
5:public:
6:staticSingleton*instance(void)
7:{
8://Firstcheck
9:if(instance_==0)
10:{
11://Ensureserialization(guard
12://constructoracquireslock_).
13:Guardguard(lock_);
14://Doublecheck.
15:if(instance_==0)
16:instance_=newSingleton;
17:}
18:returninstance_;
19://guarddestructorreleaseslock_.
20:}
21:private:
22:staticMutexlock_;
23:staticSingleton*instance_;
24:};
//C++中,可以使用volatile关键字,其中对于VC++,2005及之后的版本才可以.
1://ScottMeyers指出,单独将pInstance声明为volatile
2://是不安全的,更安全的形式如下:
3:classSingleton{
4:public:
5:staticvolatileSingleton*volatileinstance();
6://else...
7:
8:private:
9://onemorevolatileadded
10:staticvolatileSingleton*volatilepInstance;
11:};
12:
13://fromtheimplementationfile
14:volatileSingleton*volatileSingleton::pInstance=0;
15:
16:volatileSingleton*volatileSingleton::instance()
17:{
18:if(pInstance==0)
19:{
20://Locklock;根据具体的库实现
21:Locklock;
22:if(pInstance==0)
23:{
24://onemorevolatileadded
25:
26:volatileSingleton*volatiletemp=
27:newvolatileSingleton;
28:pInstance=temp;
29:}
30:}
31:returnpInstance;
32:}
1://使用boost库,典型的可能如下--(不了解boost库)
2:staticSingleton*getInstDC()
3:{
4:if(inst_==0)
5:{
6:boost::mutex::scoped_lockl(guard_);
7:if(inst_==0){
8:inst_=newSingleton();
9:}
10:
11:returninst_;
12:}
13:}
1://使用boost库,另有人给出的可能实现方法如下:
2:template
3:classCMTSingleton
4:{
5:private:
6:classCreator
7:{
8:public:
9:Creator()
10:{
11:CMTSingleton<_Tp>::instance();
12:}
13:inlinevoidDoNothing()const{}
14:}
15:
16:staticCreatorcreator;
17:};
18:
19:template
20:typenameCMTSingleton<_Tp>::CreatorCMTSingleton<_Tp>::creator;
一直也没有使用过多线程,不知上面的方法具体情况会不会有问题.
看到一个使用比较完整的实现,贴出来,里面我进行了一些语法上的修改,因为原代码编译通不过,VS2008
1://Double-CheckedLocking实现:
2://同步类:
3://synobj.h
4:
5:#ifndefSYNOBJ_H
6:#defineSYNOBJ_H
7:
8:#include
9:
10://用宏将赋值和拷贝构造函数设为私有且不实现
11:#defineCLASS_UNCOPYABLE(classname)/
12:private:/
13:classname##(constclassname##&);/
14:classname##&operator=(constclassname##&);
15:
16:classMutex{
17:CLASS_UNCOPYABLE(Mutex)
18:
19:public:
20:Mutex():_cs()
21:{
22:InitializeCriticalSection(&_cs);
23:}
24:~Mutex()
25:{
26:DeleteCriticalSection(&_cs);
27:}
28:voidlock()
29:{
30:EnterCriticalSection(&_cs);
31:}
32:voidunlock()
33:{
34:LeaveCriticalSection(&_cs);
35:}
36:
37:private:
38:CRITICAL_SECTION_cs;//临界区
39:};
40:
41:classLock
42:{
43:CLASS_UNCOPYABLE(Lock)
44:
45:public:
46://explicit,可以阻止隐式转换的发生
47:explicitLock(Mutex&cs):_cs(cs)
48:{
49:_cs.lock();
50:}
51:
52:~Lock()
53:{
54:_cs.unlock();
55:}
56:
57:private:
58:Mutex&_cs;//互斥变量
59:};
60:
61:#endif/*SYNOBJ_H*/
1://有了同步对象很容易就能够写出如下代码:
2://singleton.h
3:
4:#ifndefSINGLETON_H
5:#defineSINGLETON_H
6:
7:#include"synobj.h"
8:
9:classSingleton
10:{
11:public:
12:staticvolatileSingleton*Instance()
13:{
14://Uniquepointofaccess
15:if(0==_instance)
16:{
17://因为lock只对0==_instance为true才有效
18://所以为了优化就再加入了一个判断。
19:Locklock(_mutex);
20:if(0==_instance)
21:{
22:_instance=newSingleton();
23:atexit(Destroy);
24://RegisterDestroyfunction
25:}
26:
27:}
28:
29:return_instance;
30:}
31:
32:voidDoSomething(){}
33:
34:private:
35:staticvoidDestroy()
36:{
37://Destroytheonlyinstance
38:if(_instance!=0)
39:{
40:delete_instance;
41:_instance=0;
42:}
43:}
44:Singleton(){}//PreventclientsfromcreatinganewSingleton
45:~Singleton(){}//PreventclientsfromdeletingaSingleton
46:Singleton(constSingleton&);//PreventclientsfromcopyingaSingleton
47:Singleton&operator=(constSingleton&);
48:
49:private:
50:staticMutex_mutex;
51:staticSingletonvolatile*_instance;//Theoneandonlyinstance
52:
53://为了防止编译器优化加入volatile这个修饰关键字
54:};
55:
56:#endif/*SINGLETON_H*/
1://singleton.cpp
2:
3:#include"singleton.h"
4:
5:MutexSingleton::_mutex;
6:
7://为了防止编译器优化加入volatile这个修饰关键字
8:volatileSingleton*Singleton::_instance=0;
Java代码,注意,这个是有问题的.正确的在后面:
1:publicclassUSTaxextendsTax
2:{
3:privatestaticUSTaxinstance;
4:privateUSTax(){}
5:publicstaticTaxgetInstance(){
6:if(instance==null){
7://检查到null之后同步,然后再检查一次,确保实例尚未创建
8:synchronied(this){
9:if(instance==null)
10:instance=newUSTax();
11:}
12:}
13:returninstance;
14:}
15:
16:privatesyschronizedstaticvoiddoSync(){
17:if(instance==null)
18:instance=newUSTax();
19:}
20:};
对Java不太了解,只把代码贴出来,以防以后参考.上面的方法是有问题的,因为Java编译器本身的优化工作会在构造方法实例化对象之前从构造方法返回指向该对象的引用.因此,在USTax对象真正完全构造之前.doSync就可能完成了.这会带来问题.而且编译器会"注意到""实例"成员"没有办法"在两个if语句之间改变状态,所以会优化掉第二个.可以如下解决这一问题:利用类装载程序:
1:publicclassUSTaxextendsTax
2:{
3:privatestaticclassInstance{
4:staticfinalTaxinstance=newUSTax();
5:}
6:privatestaticUSTaxinstance;
7:privateUSTax(){}
8:publicstaticTaxgetInstance(){
9:returnInstance.instance;
10:}
11:};
因为内部类(Instance)将只被装载一次,所以只会创建一个USTax对象.
相关文章推荐
- Singleton 单件模式及其变体 Double-Checked Locking 双重检查锁模式
- Design Pattern_Singleton(单件模式)和Double-Checked Locking(双重检查锁定)
- Singleton(单例)模式和Double-Checked Locking(双重检查锁定)模式
- Singleton - 单例模式和Double-Checked Locking - 双重检查锁定模式
- Double-checked locking and the Singleton pattern--双重检查加锁失效原因剖析
- 双重检查锁定(double-checked locking)与单例模式
- 双重检查锁定(double-checked locking)与单例模式
- 深刻理解双重检查锁定(double-checked locking)与单例模式
- 双重检查锁定(double-checked locking)与单例模式
- 双重检查锁定(double-checked locking)与单例模式
- Java设计模式之单例模式 double---checked----locking双重检查锁定
- c++中的 单例模式(singleton)和双检测锁(Double-Checked Locking)
- [zt]Singleton和Double-Checked Locking设计模式—UML图及代码实现
- 单例模式中的 双重检查锁定(Double-Check Locking ) (多线程下单例模式中的双重检查锁定的实现)
- 单例模式中的 双重检查锁定(Double-Check Locking ) (多线程下单例模式中的双重检查锁定的实现)
- Java中的双重检查锁(double checked locking)
- 【线程安全】—— 单例类双重检查加锁(double-checked locking)
- 【Java】双重检查锁定(Double-checked locking)与延迟初始化(Initialization on demand holder)
- 单例模式与双重检测(Double-Checked Locking, DCL)
- (设计模式)Singleton和Double-Checked Locking模式