单例模式
单例模式是最简单的一种模式了相对于其他模式,他的主要目的就是为了保证一个类只能有一个实例。这在编程当中也是非常常见的。比方说window任务管理器,就只能开一个,比方说我们开发的时候弹出的窗体,也只允许有一个。
我们常见的创建对象的方法是直接new一个,如下:
Singleton instance=new Singleton();
这种方式的结果的,在不同的环境中或者在同一个个环境中我们想new几个就new几个,对象的产生是自由的,要想产生单个对象,可以通过私有构造器来实现,我们可以写一个方法,在每次new之前,先判断一下,如果没有这个对象,就new一个,如果有就不需要再创建了。如下:
class Singleton
{
private static Singleton instance;
private Singleton()
{
}
private static Singleton CreateInstance()
{
if(instance==null)
{
instance = new Singleton();
}
return instance;
}
}
上述方法再单线程下几乎是很完美了,但是我们也需要注意几点,单例模式的创建方法只管对象的创建,至于该对象的销毁,一般由clr自动回收的。,当然有些非托管的资源在销毁的时候可能需要手动销毁掉。
需要注意的是单例模式不应该支持ICloneable接口,这可能会导致多个对象得实例。(该接口是实例的克隆接口,在拷贝对象的时候同样可能还是会产生多个实例,也就违背单例模式的初衷了)。单例模式也不应该支持序列化操作,因为对象的创建除了通过构造器以外,也可以通过序列化实现,这样也会出现与单例模式想悖的情况。还有一个就是多线程,很多情况下,在多线程环境中就得考虑并发,比方我启动两个线程,这两个线程同时启动也可能产生两个实例,还是违背了单例的初衷,而且在并发访问资源的同时,会出现各种意想不到的问题,解决办法还是加锁。代码如下:
class Singleton
{
public static volatile Singleton instance;//多任务环境下各任务间共享的标志volatile
private static object obj = new object();
private Singleton()
{
}
private static Singleton CreateInstance()
{
if (instance == null)//先检查实例是否存在,如果不存在才进入下面的同步块
{
lock (obj)//同步块,线程安全的创建实例
{
if (instance == null)//再次检查实例是否存在,如果不存在创建实例
{
instance = new Singleton();
}
}
}
return instance;
}
}
这里使用了双重判定,需要说明一下,之所以使用两个if,是因为在多线程中(比方说两个线程),由于刚开始的时候,假设两个线程同时到达,同时调用CreateInstatce()方法,这个时候两个instance都为null,那么两个线程都会通过第一重if语句instance == null判断,当进入第一个if语句后,由于加锁的存在,第二个线程在外等待,第一个线程进入第二重判断,这个时候new出一个instance对象,然后执行结束,第一个线程退出锁,这个时候第二个线程进入锁,如果不对他进行判定instace==null的话,他还是会直接执行instance = new Singleton();这句话,相当于又new出了一个instance对象,还是违背了单例的设计模式的初衷。当然,这个如果细心考虑的话,如果用第二个if就行了,因为,在第二个if执行的时候,一个线程new出一个instance实例,当他退出的时候,在内存里已经有一个instance实例了,由于静态资源,也不会立马释放,当第二个线程进入时候,由于instance不为空,所以第二个if其实肯定进不去,但是这里再判断一次,其实是考虑多线程情况下,线程同步执行的情况下的性能问题,想象一下,第二个线程进入的时候,其实资源已经不为空了,这个时候我也没必要在加锁判断了,只需要在锁外面判断就行了,加锁和解锁本身就是耗费性能的。