单件模式 Single Pattern 独一无二的对象
定义:单件模式确保程序中一个类最多只有一个实例,并提供全局访问。
实现单件模式需要私有的构造器、一个静态方法和一个静态变量。
确定在性能和资源上的限制,然后小心的选择适当的方案实现单件,以解决多线程问题(我们必须认定所有的程序都是多线程的)。
哪里会用到单件模式:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置、注册表对象(registry)的对象、日志对象,充当打印机、显卡等设备的驱动程序对象。
实现单件模式
我们创建一个对象是这样的:通过调用类的构造方法 new Object (),一般类的构造方法访问修饰符是public,要在类里控制控制它自己的实例个数首先要把它的构造方法写成private的。这样它不能被别的类实例化而只能在自己内部实例化,就有控制的可能。一个不能被外界实例化的类只有通过类调用自己的静态方法实例化:一个简单的标准的单件类:
{
private MyClass(){}
public static MyClass getInstance()
{
return new MyClass();
}
}
这样还不够,还不能控制实例的个数,还要再加一个私有的静态MyClass变量,getInstane方法中控制数量
{
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.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。这里与全局变量相比利用了延迟实例化的方式,这种方式对资源敏感的对象很重要。
{
if (uniqueInstance == null )
{
lock (syncRoot)
{
if (uniqueInstance == null )
{
uniqueInstance = new RateSingleton();
}
}
}
return uniqueInstance;
}
如果你的应用程序可以接受同步getInstance()的负担可以使用下名的版本,不过要是getInstancez在频繁允许的地方,这个方法就行不通了。因为下边的方法是每次调用都会同步的。
{
if (uniqueInstance == null )
{
uniqueInstance = new RateSingleton();
}
return uniqueInstance;
}
如果你的程序有多个类的加载器同时又使用了单件模式有时会出问题的:每个类的加载器都定义了一个命名空间,不同的类加载器可能会加载同一个类,从整个程序看,同一个类被加载了多次。如果这事发生在单件上,就会产生多个单件并存的怪异现象。解决办法:自行指定类加载器,并指定同一个。
附:Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java c#语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。