单件模式 Single Pattern 独一无二的对象
定义:单件模式确保程序中一个类最多只有一个实例,并提供全局访问。
实现单件模式需要私有的构造器、一个静态方法和一个静态变量。
确定在性能和资源上的限制,然后小心的选择适当的方案实现单件,以解决多线程问题(我们必须认定所有的程序都是多线程的)。
哪里会用到单件模式:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置、注册表对象(registry)的对象、日志对象,充当打印机、显卡等设备的驱动程序对象。
实现单件模式
我们创建一个对象是这样的:通过调用类的构造方法 new Object (),一般类的构造方法访问修饰符是public,要在类里控制控制它自己的实例个数首先要把它的构造方法写成private的。这样它不能被别的类实例化而只能在自己内部实例化,就有控制的可能。一个不能被外界实例化的类只有通过类调用自己的静态方法实例化:一个简单的标准的单件类:
public
class
MyClass
{
private
MyClass(){}
public
static
MyClass getInstance()
{
return
new
MyClass();
}
}
这样还不够,还不能控制实例的个数,还要再加一个私有的静态MyClass变量,getInstane方法中控制数量
public
class
MyClass
{
private
MyClass(){}
private
static
MyClass uniqueInstace;
public
static
MyClass getInstance()
{
if
(uniqueInstace
==
null
){
uniqueInstace
=
new
MyClass();
}
return
uniqueInstace;
}
}
然而这样当多线程调用这段代码时很有可能出现问题:线程1、2都执行到if(uniqueInstace==null)而uniqueInstace是null,然后就会各自创建自己的实例。所以用到同步。
using
System;
using
System.Configuration;
namespace
HeadFirstDesignPatterns.Singleton.InterestRate
{
///
<summary>
///
Summary description for RateSingleton.
///
</summary>
public
class
RateSingleton
{
private
volatile
static
RateSingleton uniqueInstance;
private
static
object
syncRoot
=
new
Object();
private
double
currentRate
=
Convert.ToDouble(ConfigurationSettings.AppSettings[
"
CurrentInterestRate
"
]);
public
double
CurrentRate
{
get
{
return
currentRate;
}
set
{
currentRate
=
value;
}
}
private
RateSingleton()
{}
public
static
RateSingleton GetInstance()
{
//
The approach below ensures that only one instance is created and only
//
when the instance is needed. Also, the uniqueInstance variable is
//
declared to be volatile to ensure that assignment to the instance variable
//
completes before the instance variable can be accessed. Lastly,
//
this approach uses a syncRoot instance to lock on, rather than
//
locking on the type itself, to avoid deadlocks.
if
(uniqueInstance
==
null
)
{
lock
(syncRoot)
{
if
(uniqueInstance
==
null
)
{
uniqueInstance
=
new
RateSingleton();
}
}
}
return
uniqueInstance;
}
}
}
主要代码:
private volatile static RateSingleton uniqueInstance;//声明一个静态的私有的本类实例,volatile关键字见附。
private RateSingleton(){}//提供私有的构造方法
//用"双重检查加锁"减少使用同步提高单件执行的率。如果是java则要求是java5。这里与全局变量相比利用了延迟实例化的方式,这种方式对资源敏感的对象很重要。
public
static
RateSingleton GetInstance()
{
if
(uniqueInstance
==
null
)
{
lock
(syncRoot)
{
if
(uniqueInstance
==
null
)
{
uniqueInstance
=
new
RateSingleton();
}
}
}
return
uniqueInstance;
}
如果你的应用程序可以接受同步getInstance()的负担可以使用下名的版本,不过要是getInstancez在频繁允许的地方,这个方法就行不通了。因为下边的方法是每次调用都会同步的。
public
static
synchronized RateSingleton GetInstance()
{
if
(uniqueInstance
==
null
)
{
uniqueInstance
=
new
RateSingleton();
}
return
uniqueInstance;
}
如果你的程序有多个类的加载器同时又使用了单件模式有时会出问题的:每个类的加载器都定义了一个命名空间,不同的类加载器可能会加载同一个类,从整个程序看,同一个类被加载了多次。如果这事发生在单件上,就会产生多个单件并存的怪异现象。解决办法:自行指定类加载器,并指定同一个。
附:Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java c#语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
有关HeadFirst 设计模式的全部代码可以去我的资源里下载(Java C#两种版本):http://download.csdn.net/user/jjjjj102310253
大尾巴狼 2008.4.17