c#设计模式(1)——单例模式

一、引言

       最近看了一些单例模式的介绍,感觉可以总结一下,形成自己的笔记。部分内容选自https://www.cnblogs.com/zhili/p/SingletonPatterm.html,感觉写得很好,我只是再加点东西

二、介绍

      单例模式(Singleton Pattern)是最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

      这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

     注意:

          1、单例类只能有一个实例。

          2、单例类必须自己创建自己的唯一实例。

          3、单例类必须给所有其他对象提供这一实例。

三、多种单例模式的创建方式

    1.饿汉式创建单例模式

     说明:饿汉就是类一旦加载,就把单例初始化完成,保证CreateSingleton的时候,单例是已经存在的了。饿汉式创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。

     

    /// <summary>
    /// 饿汉式写法
    /// </summary>
    public class SingletonEH
    {
        /**
         *是否 Lazy 初始化:否
         *是否多线程安全:是
         *实现难度:易
         *描述:这种方式比较常用,但容易产生垃圾对象。
         *优点:没有加锁,执行效率会提高。
         *缺点:类加载时就初始化,浪费内存。
         *它基于 classloder 机制避免了多线程的同步问题,
         * 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
         * 在单例模式中大多数都是调用 CreateSingleton 方法,
         * 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
         * 这时候初始化 instance 显然没有达到 lazy loading 的效果。
        */
        private static SingletonEH instance = new SingletonEH();

        private SingletonEH()
        {
            Console.WriteLine($"{typeof(SingletonEH).Name}被构造。。。");
        }
        public static SingletonEH CreateSingleton()
        {
            return instance;
        }
    }

  2.懒汉式创建单例模式

        说明:而懒汉比较懒,只有当调用CreateSingleton的时候,才回去初始化这个单例。懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

        

 /// <summary>
    /// 懒汉式
    /// </summary>
    public class SingletonLH
    {
        /**
     *是否 Lazy 初始化:否
     *是否多线程安全:是
     *实现难度:易
     *描述:这种方式比较常用,但容易产生垃圾对象。
     *优点:没有加锁,执行效率会提高。
     */
        private static SingletonLH instance = null;

        private SingletonLH()
        {
            Console.WriteLine($"{typeof(SingletonLH).Name}被构造。。。");
        }

        public static SingletonLH CreateInstance()
        {
            if(instance == null)
            {
                 return new SingletonLH();
             }
            return instance;
        }
    }

  3.上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行CreateInstance方法时,此时两个线程判断(instance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使CreateInstance方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了。

       

  public class SingletonLH
    {
        /**
     *是否 Lazy 初始化:否
     *是否多线程安全:是
     *实现难度:易
     *描述:这种方式比较常用,但容易产生垃圾对象。
     *优点:没有加锁,执行效率会提高。
     *缺点:类加载时就初始化,浪费内存。
     *它基于 classloder 机制避免了多线程的同步问题,
     * 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
    * 在单例模式中大多数都是调用 CreateInstance 方法,
     * 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
     * 这时候初始化 instance 显然没有达到 lazy loading 的效果。
     */
        private static SingletonLH instance = null;

        private static readonly object locker = new object();
        private SingletonLH()
        {
            Console.WriteLine($"{typeof(SingletonLH).Name}被构造。。。");
        }

        public static SingletonLH CreateInstance()
        {
             lock (locker)
             {
                 if(instance == null)
                {
                     return new SingletonLH();
                 }
            }
            return instance;
        }
    }

  上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(instance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(instance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”

  

  public class SingletonLH
    {
      
        private static SingletonLH instance = null;

        private static readonly object locker = new object();
        private SingletonLH()
        {
            Console.WriteLine($"{typeof(SingletonLH).Name}被构造。。。");
        }

        public static SingletonLH CreateInstance()
        {
            if(instance == null)
            {
                lock (locker)
                {
                    if(instance == null)
                    {
                        return new SingletonLH();
                    }
                }
            }
            return instance;
        }
    }

  4. 泛型封装单例模式,利于重复利用

         a.新建一个类 BaseSingleton类

          

 public class BaseSingleton<T> where T: new()
    {
        protected static T _instance;
        private static readonly object locker = new object();

        public static T getInstance()
        {
            if(_instance == null)
            {
                lock (locker)
                {
                    if(_instance == null)
                    {
                        _instance = new T();
                    }
                }
            }
            return _instance;
        }
    }

  b.需要用到的类继承调用

      

 public class Singleton: BaseSingleton<Singleton>
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public static void Test()
        {
            Console.WriteLine("this is Test...");
        }
    }

  

      

      

posted @ 2020-09-15 14:33  小贤k  阅读(276)  评论(0编辑  收藏  举报