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

    刚正式开始学习设计模式,之前有看过,但是还是觉得亲自写一遍所学的设计模式,并做笔记,这样来说,对于设计模式的理解会更深刻一些。故做笔记,如果有描述不正确的地方欢迎指出。

    1. 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

      通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法

     class Singleton
        {
            private static Singleton instance;
            //构造方法让其private,这就堵死了外界利用new创建此实例的可能
            private Singleton()
            {
            }
            //此方法是获得本类实例的唯一全局访问点
            public static Singleton GetInstance()
            {
                //若实例不存在,则new一个实例,否则返回已有的实例
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    View Code

    客户端代码

    static void Main(string[] args)
            {
                Singleton s1 = Singleton.GetInstance();
                Singleton s2 = Singleton.GetInstance();
                if (s1==s2)
                {
                    Console.WriteLine("两个对象是相同的实例。");
                }
                Console.Read();
            }
    View Code

    单利模式的好处:1.可以保证唯一的实例2.因为Singleton类它的唯一实例,这样可以严格地控制客户怎样访问它及何时访问它,简单地说就是对唯一实例的受控访问。

    单例类于实用类的静态方法的区别: 实用类通常会采用私有化的构造方法来避免其有实例。但是他们还是有不同的,比如实用类不用保存状态,仅供一些静态方法或静态属性让你实用,而单例类是有状态的。实用类不能用于继承多态,而单例虽然实例唯一,却是可以有子类来继承。实用类只不过是一些方法属性的集合,而单例却是有着唯一的对象实例。 

       2.多线程的单例

    多线程的程序中,多个线程同时,注意是同时访问Singleton类,调用GetInstance()方法,会有可能创建多个实例的。

    解决这个问题的方式,可以给进程一把锁来处理。lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

     class Singleton
        {
            private static Singleton instance;
            private static readonly object syncRoot = new object();//程序运行时创建一个静态只读的进程辅助对象
            private Singleton()
            {
            }
            public static Singleton GetInstance()
            {
                lock (syncRoot)//在同一个时刻加了锁的那部分程序只有一个线程可以进入
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
                return instance;
            }
        }
    View Code

    这段代码使得对象实例由最先进入的那个线程创建,以后的线程在进入时不会再去创建对象实例了。由于有了lock,就保证了多线程环境下的同时访问也不会造成多个实例的生成。


    这里加锁时,instance实例有没有被创建过实例都不知道,直接加锁,这样不太好。每次调用GetInstance方法时都需要lock。所以需要对其进行改良

    3.双重锁定

     1   class Singleton
     2     {
     3         private static Singleton instance;
     4         private static readonly object syncRoot = new object();
     5         private Singleton()
     6         { 
     7         }
     8         public static Singleton GetInstance()
     9         {
    10             //现在这样,我们不用让线程每次都加锁,而事实在实例未被创建的时候再加锁处理。
    11             //同时也能保证多线程的安全。这样做法被称为Double-Check Locking(双重锁定)
    12             if (instance==null)//先判断实例是否存在,不存在,再加锁处理
    13             {
    14                 lock (syncRoot)
    15                 {
    16                     if (instance==null)
    17                     {
    18                         instance = new Singleton();
    19                     } 
    20                 }
    21             }
    22             return instance;
    23         }
    24     }
    View Code

    问题1:外面已经判断了instance实例是否存在,为什么在lock里面还需要做一次instance实例是否存在的判断呢?

    答:对于instance存在的情况,就直接返回,这没有问题。当instance为null并且同时有两个线程调用GetInstance()方法时。它们将都可以
    通过第一重instance==null的判断。然后由于lock机制,这两个线程则只有一个进入,另一个在外排队等候,必须要其中的一个进入并出来后,另
    一个才能进入。而此时如果没有了第二重的instance是否为null的判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,
    这就没有达到单例的目的

     4.静态初始化

       在时间应用当中,C#与公共语言运行库也提供了一种‘静态初始化’方法,这种方法不需要开发人员显示地编写线程安全代码,即可解决多线程环境下它是不安全的问题。谈不上更好,但是实现更简单。

     1   //sealed组织发生派生,而派生可能会增加实例
     2     public sealed class Singleton
     3     {
     4         //在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化
     5         private static readonly Singleton instance = new Singleton();
     6         private Singleton() { }
     7         public static Singleton GetInstance()
     8         {
     9             return instance;
    10         }
    11     }
    View Code


    sealed 修饰符可以应用于类、实例方法和属性。密封类不能被继承。密封方法会重写基类中的方法,但其本身不能在任何派生类中进一步重写。

       这样的实现与前面的示例类似,也解决了单例模式视图解决的两个基本问题:全局访问和实例化控制,公共静态属性为访问实例提供了一个全局访问点。不同之处在于它依赖公共语言运行库来初始化变量。犹豫构造方法是私有的,因此不能在类本身以外实例化Singleton类;因此,变量引用的是可以在系统中存在的唯一的实例。不过要注意,instance变量标记为readonly,这意味着只能在静态初始化期间或在类构造函数中分配变量。由于这种静态初始化的方式在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类,原先的单例模式处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。

    饿汉式:即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。

    懒汉式,面临着多线程访问的安全性问题,需要做双重锁定这样的处理才会可以保证安全。

    所以,到底使用哪一种方式,取决于实际的需求。从C#语言角度来讲,饿汉式的单例类已经足够满足我们的需求了

  • 相关阅读:
    avalon.js 多级下拉框实现
    提取 ECharts 中的svg地图信息
    avalon.js实践 svg地图配置工具
    基于HTML Canvas实现“指纹识别”技术
    HTML5 Canvas指纹及反追踪介绍
    百度Openrasp开源的应用运行时自我保护产品,安装教程。
    浏览器指纹实现
    centos监控web目录www下的文件是否被黑、挂马的脚本
    Linux下文件完整性监控工具Tripwire详解
    在CentOS 7上使用Tripwire监控和检测修改的文件
  • 原文地址:https://www.cnblogs.com/xiaoxiaomi/p/5820022.html
Copyright © 2011-2022 走看看