当前位置:首页 > 后端 > .net > 正文内容

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

放牧的风7年前 (2018-02-12).net1120

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

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

image_thumb_1.png

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

  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# 中使用静态初始化实现饿汉式单例类。


扫描二维码推送至手机访问。

版权声明:本文由放牧的风发布,如需转载请注明出处。

本文链接:https://grazingwind.com/post/6.html

分享给朋友: