设计模式学习之单件模式(Singleton)
作用:保证一个类只能有一个实例。并提供一个访问它的全局访问点。属于创建型模式。
实现要点:一.使用一个静态成员变量作为“全局”实例,这样就确保了唯一性
二.使用静态的成员函数instance()替代new来获取该类的实例,这样就提供了全局的访问点
三.构造函数设为private,使调用者不能用new来创建此类的实例
代码:两种方法实现单件模式,一种是Early initialization,一种是Lazy Initialization。
方法一:
class EarlySingleton
{
private static EarlySingleton earlyInstance = new EarlySingleton();
private EarlySingleton()
{
}
public static EarlySingleton Instance()
{
return earlyInstance;
}
}
方法二:
class LazySingleton
{
private static LazySingleton lazyInstance;
private LazySingleton()
{
}
public static LazySingleton Instance()
{
if (lazyInstance == null)
{
lazyInstance = new LazySingleton();
}
return lazyInstance;
}
}
深入:观察代码可以看出,方法二的静态变量只有在需要使用的时候才实例化,与方法一不管什么时候都一直存在相比,更加节省系统资源。但是方法二也不是没有问题——当使用多线程时,由于可能的线程异步,某个线程已经开始实例化lazyInstance但还未完成,而另一个线程在判断lazyInstance == null时仍然为true,从而又实例化一个,这将招致严重的错误。解决方法有三个:这三个方法是:
1、使用 [MethodImpl(MethodImplOptions.Synchronized)] ,指定instance()方法同时只能被一个线程使用
2、lock(myObject),是对一个对象加互斥锁,只允许一个线程访问其后大括号中语句块,直到该语句块的代码执行完才解锁,解锁后才允许其他的线程执行其语句块。
3、使用 Mutex 类的 WaitOne 方法。
按这三个方法修改后的代码分别如下:
1.
using System.Runtime.CompilerServices;
//Lazy Initialization
class LazySingleton
{
private static LazySingleton lazyInstance;
private LazySingleton()
{
}
[MethodImpl(MethodImplOptions.Synchronized)] //方法的同步属性
public static LazySingleton Instance()
{
if (lazyInstance == null)
{
lazyInstance = new LazySingleton();
}
return lazyInstance;
}
}
2.
//Lazy Initialization
class LazySingleton
{
private static LazySingleton lazyInstance;
static object myObject = new object();
private LazySingleton()
{
}
public static LazySingleton Instance()
{
lock (myObject)
{
if (lazyInstance == null)
{
lazyInstance = new LazySingleton();
}
return lazyInstance;
}
}
}
3.
//Lazy Initialization
class LazySingleton
{
private static LazySingleton lazyInstance;
static object myObject = new object();
private static Mutex mut = new Mutex();
private LazySingleton()
{
}
public static LazySingleton Instance()
{
mut.WaitOne();
if (lazyInstance == null)
{
lazyInstance = new LazySingleton();
}
mut.ReleaseMutex();
return lazyInstance;
}
}
发现很多设计模式的作者都是写到此为止。其实还未结束,James.W.Cooper在他的书里指出:当唯一的实例已经存在时,最好给调用者抛出一个明确的异常信息。这个很有道理,如果单件实例已存在,而调用者不知道,我们不吭声就把这个已存在的单件实例返回给他,很有可能导致他犯更大的错误。
修改Instance()中代码:
{
lazyInstance = new LazySingleton();
}
else
{
throw new Exception("Singleton should be init just once");
}