zoukankan      html  css  js  c++  java
  • 单例模式

    定义:

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

    解决问题:

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

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

    结构图:

      

      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,只能在静态初始化期间或在类构造函数中分配变量。

      

  • 相关阅读:
    Linux内核TSS的使用
    DPL, CPL及RPL之间的关系
    Linux内存管理(深入理解Linux内核)
    Windows下安装PIL进行图像处理
    内存Zone中的pageset成员分析
    导出符号的意义
    GDI及Windows的消息机制
    kmalloc vs vmalloc
    Linux Kernel Development有关内存管理
    STL sort
  • 原文地址:https://www.cnblogs.com/panpanwelcome/p/5594386.html
Copyright © 2011-2022 走看看