标签:
定义:
保证一个类仅有一个示例,并提供一个访问它的全局访问点。
解决问题:
通常可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。
如果需要只实例化一个对象,最好的办法是让类自身负责保存它的唯一示例。这个类可以抱枕没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
结构图:
Singleton:
客户端:
优点:
可以保证唯一的实例。
可以严格地控制客户怎样访问它,以及何时访问它。即对唯一实例的受控访问。
与实用类的异同:
实用类:如.NET框架里的Math类 。
类似:
实用类通常也会采用私有化的构造方法来避免使其有实例。
不同:
实用类不保存状态,仅提供一些静态方法、静态属性让客户端调用。而单例是有状态的。
实用类不能用于继承多态,而单例虽然实例唯一,却可以有子类来继承。
实用类只不过是一些方法、属性的结合,而单例却是有着唯一的对象实例。
示例:
因为构造方法私有,只能内部调用。外部需要实例化类,只能通过访问静态方法GetInstance(),就不会出现超生情况。
客户端不在考虑只用实例化一次的问题,而是把责任都给了应该负责的类去处理。
客户端调用:
对比:
关闭工具箱时,实例并没有变为null,而只是Disposed。
多线程时的单例:
多线程同时访问Singleton类,调用GetInstance(),可能会造成创建多个实例。
如何解决?
方式一:
可以给进程加锁。
使用lock,保证了多线程环境下的同时访问也不会造成多个实例的生成。
保证对象实例由最先进入的那个线程创建,以后的线程不会再去创建对象实例了。
为什么不直接lock(instance),而是创建一个syncRoot来lock?
加锁时,instance实例有没有被创建过还不知道,无法对它加锁。
缺点:
每次调用GetInstance()时,都需要lock会影响性能。
优化一: 双重锁定(Double-Check Locking),不用让线程每次都加锁,而只是在实例未被创建的时候再加锁,同时,还能保证多线程的安全
外面已经判断了instance实例是否存在,为什么在lock里面还需要做一次instance实例是否存在的判断呢?
对于instance存在的情况,就直接返回,这没有问题;
当Instance为null,并且同时有两个进程调用GetInstance()时,则都可以通过第一重Instance==null的判断。然后由于lock机制,这两个线程则只有一个进入,另一个在外排队等候。必须其中一个进入并出来以后,另一个才能进入。如果没有第二重Instance==null的判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,就没有达到单例的目的。
优化二:
静态初始化:
由C#与公共语言运行库提供。
不需要显式地编写线程安全代码,即可解决多线程环境下不安全的问题。
解决了全局访问和实例化控制,公共静态属性为访问提供了一个全局访问点。
优点:实现更简单。
实现原理:
静态初始化,在于它依赖公共语言运行库来初始化变量。
构造方法私有,不能再类本身以外实例化Singleton类,是可以在系统中存在的唯一的实例。
instance变量标记为readonly,只能在静态初始化期间或在类构造函数中分配变量。
不同之处:
此方式为饿汉式单例类,而其他方式则为懒汉式单例类。
饿汉式单例类,静态初始化的方式,是在类一被加载就实例化对象,要提前占用系统资源。
懒汉式单例类,要在第一次被引用时,才会将自己实例化,但是会面临多线程访问的安全问题,需要双重锁定才可以保证线程安全。
根据实际需求,选择使用哪一种方式(饿汉式、懒汉式)实现单例。
扩展:
复制粘贴是最容易的编程,但也是最没有价值的编程。
所有类都有构造方法,不编码则系统默认生成空的构造方法;若有显示定义的构造方法,默认的构造方法就会失效。若阻止他人实例化当前类,将当前类的构造方法写成private即可。
lock,确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程视图进入锁定的代码,则它将一直等待(被阻塞),直到该对象被释放。
变量标记为readonly,只能在静态初始化期间或在类构造函数中分配变量。
标签:
原文地址:http://www.cnblogs.com/panpanwelcome/p/5594386.html