码迷,mamicode.com
首页 > 其他好文 > 详细

单例模式

时间:2016-06-17 16:56:47      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:

定义:

  保证一个类仅有一个示例,并提供一个访问它的全局访问点。

解决问题:

  通常可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。

  如果需要只实例化一个对象,最好的办法是让类自身负责保存它的唯一示例。这个类可以抱枕没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

结构图:

  技术分享

  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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!