• C#设计模式--单例模式


    单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。

    下面来看单例模式的结构图:

    image

    从上面的类图中可以看出单例模式的特点:

    1. 在单例类中有一个构造函数 Singleton ,但是这个构造函数却是私有的
    2. 公开了一个 GetInstance()方法

    通过上面的类图不难看出单例模式的特点,从而也可以给出单例模式的定义:

    单例模式保证一个类仅有一个实例,同时这个类还必须提供一个访问该类的全局访问点。

    1.最基本的单例:

    namespace Singleton 
    { 
        public class Singleton 
        { 
            //定义一个私有的静态全局变量来保存该类的唯一实例 
            private static Singleton singleton;
    
            /// <summary> 
            /// 构造函数必须是私有的 
            /// 这样在外部便无法使用 new 来创建该类的实例 
            /// </summary> 
            private Singleton() 
            { 
            }
    
           /// <summary> 
            /// 定义一个全局访问点 
            /// 设置为静态方法 
            /// 则在类的外部便无需实例化就可以调用该方法 
            /// </summary> 
            /// <returns></returns> 
            public static Singleton GetInstance() 
            { 
                //这里可以保证只实例化一次 
                //即在第一次调用时实例化 
                //以后调用便不会再实例化 
                if (singleton == null) 
                { 
                    singleton = new Singleton(); 
                } 
                return singleton; 
            } 
        } 
    }
    

    2.线程安全单例

    namespace Singleton 
    { 
        public class Singleton 
        { 
            //定义一个私有的静态全局变量来保存该类的唯一实例 
            private static Singleton singleton;
    
            //定义一个只读静态对象 
            //且这个对象是在程序运行时创建的 
            private static readonly object syncObject = new object();
    
            /// <summary> 
            /// 构造函数必须是私有的 
            /// 这样在外部便无法使用 new 来创建该类的实例 
            /// </summary> 
           private Singleton() 
           {
    
           }
    
            /// <summary> 
            /// 定义一个全局访问点 
            /// 设置为静态方法 
            /// 则在类的外部便无需实例化就可以调用该方法 
            /// </summary> 
            /// <returns></returns> 
            public static Singleton GetInstance() 
            { 
                //这里可以保证只实例化一次 
                //即在第一次调用时实例化 
                //以后调用便不会再实例化 
    
                //第一重 singleton == null 
                if (singleton == null) 
                { 
                    lock (syncObject) 
                    {
                        //第二重 singleton == null
                        if (singleton == null) 
                        { 
                            singleton = new Singleton(); 
                        } 
                    } 
                } 
                return singleton; 
            } 
        } 
    }
    • 为何要使用双重检查锁定呢?

    考虑这样一种情况,就是有两个线程同时到达,即同时调用 GetInstance(),

    此时由于 singleton == null ,所以很明显,两个线程都可以通过第一重的 singleton == null ,

    进入第一重 if 语句后,由于存在锁机制,所以会有一个线程进入 lock 语句并进入第二重 singleton == null ,

    而另外的一个线程则会在 lock 语句的外面等待。

    而当第一个线程执行完 new  Singleton()语句后,便会退出锁定区域,此时,第二个线程便可以进入 lock 语句块,

    此时,如果没有第二重 singleton == null 的话,那么第二个线程还是可以调用 new  Singleton()语句,

    这样第二个线程也会创建一个 Singleton 实例,这样也还是违背了单例模式的初衷的,

    所以这里必须要使用双重检查锁定

     

    • 其实在没有第一重 singleton == null 的情况下,也是可以实现单例模式的,那么为什么需要第一重 singleton == null 呢?

    这里就涉及一个性能问题了,因为对于单例模式的话,new Singleton()只需要执行一次就 OK 了,

    而如果没有第一重 singleton == null 的话,每一次有线程进入 GetInstance()时,均会执行锁定操作来实现线程同步,

    这是非常耗费性能的,而如果我加上第一重 singleton == null 的话,

    那么就只有在第一次,也就是 singleton ==null 成立时的情况下执行一次锁定以实现线程同步,

    而以后的话,便只要直接返回 Singleton 实例就 OK 了而根本无需再进入 lock 语句块了,这样就可以解决由线程同步带来的性能问题了。

    好,关于多线程下单例模式的实现的介绍就到这里了,但是,关于单例模式的介绍还没完。

    下面将要介绍的是懒汉式单例和饿汉式单例

    1.懒汉式单例

    何为懒汉式单例呢,可以这样理解,单例模式呢,其在整个应用程序的生命周期中只存在一个实例,

    懒汉式呢,就是这个单例类的这个唯一实例是在第一次使用 GetInstance()时实例化的,

    如果您不调用 GetInstance()的话,这个实例是不会存在的,即为 null

    形象点说呢,就是你不去动它的话,它自己是不会实例化的,所以可以称之为懒汉。

    其实呢,我前面在介绍单例模式的这几个 Demo 中都是使用的懒汉式单例

    从前面的这个 GetInstance()中可以看出这个单例类的唯一实例是在第一次调用 GetInstance()时实例化的,

    所以此为懒汉式单例。     

    2.饿汉式单例

    上面介绍了饿汉式单例,到这里来理解懒汉式单例的话,就容易多了,懒汉式单例由于人懒,

    所以其自己是不会主动实例化单例类的唯一实例的,而饿汉式的话,则刚好相反,

    其由于肚子饿了,所以到处找东西吃,人也变得主动了很多,所以根本就不需要别人来催他实例化单例类的为一实例,

    其自己就会主动实例化单例类的这个唯一类。

    在 C# 中,可以用特殊的方式实现饿汉式单例,即使用静态初始化来完成饿汉式单例模式

    下面就来看一看饿汉式单例类

    namespace Singleton 
    { 
        public sealed class Singleton 
        { 
            private static readonly Singleton singleton = new Singleton();
    
    
            private Singleton() 
            { 
            }
    
    
            public static Singleton GetInstance() 
            { 
                return singleton; 
            } 
        } 
    }

    要先在这里提一下的是使用静态初始化的话,无需显示地编写线程安全代码,

    C# 与 CLR 会自动解决前面提到的懒汉式单例类时出现的多线程同步问题。

    上面的饿汉式单例类中可以看到,当整个类被加载的时候,就会自行初始化 singleton 这个静态只读变量。

    而非在第一次调用 GetInstance()时再来实例化单例类的唯一实例,所以这就是一种饿汉式的单例类。

    好,到这里,就真正的把单例模式介绍完了,在此呢再总结一下单例类需要注意的几点:

    一、单例模式是用来实现在整个程序中只有一个实例的。

    二、单例类的构造函数必须为私有,同时单例类必须提供一个全局访问点。

    三、单例模式在多线程下的同步问题和性能问题的解决。

    四、懒汉式和饿汉式单例类。

    五、C# 中使用静态初始化实现饿汉式单例类。

  • 相关阅读:
    超全面的vue.js使用总结
    Python3 [字典】类型 学习笔记
    Python3 [集合]类型 学习笔记
    Python 希尔排序法
    Python 堆排序法
    Python 归并排序法
    Python 冒泡排序法
    Python 选择排序法
    Python 快速排序法(转)
    Python 插入排序法
  • 原文地址:https://www.cnblogs.com/dxxzst/p/8444208.html
Copyright © 2020-2023  润新知