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

    有点编程经验的人应该都知道单例模式,属于创建型模式,定义也挺简单,一个类有且仅有一个实例,并且提供一个全局访问点。

    根据定义写一个单例类挺简单的,比如这样子:

        public sealed class SignletonOne
        {
            private SignletonOne() { }
    
            private static SignletonOne _instance;
            public static SignletonOne Instance
            {
                get
                {
                    return _instance ?? new SignletonOne();
                }
            }
        }
    

      ok  单例模式写好了,属于懒汉加载,但是不是太优秀,因为这种写法能在单线程的条件下正常使用,但是多线程就有问题了,比如两个线程同时运行到判断的时候 问题就出来了,就不满足一个类有且仅有一个实例。那好,接着往下优化,首要目标就是多线程情况下可以正常使用,代码如下:

        public sealed class SignletonTwo
        {
            private SignletonTwo() { }
    
            private readonly static object objLock = new object();
    
            private static SignletonTwo _instance;
            public static SignletonTwo Instance
            {
                get
                {
                    lock (objLock)
                    {
                        _instance= _instance ?? new SignletonTwo();
                    }
                    return _instance;
                }
            }
        }

    好的,加上了lock,在同一时刻只能有一个线程能得到同步锁,也就是说 当第一个线程加上锁的时候,第二个线程只能够等待这样子就保证了在多线程的情况下 只能有一个实例。

    但是这种写法也不是太好,为什么呢?因为我们每次通过属性Instance去得到实例的时候,都会去进行加锁操作,我们已经有个实例了,为什么还要这部操作呢?多余,接着优化:

        public sealed class SignletonThree
        {
            private SignletonThree() { }
    
            private static Object objLock = new object();
    
            private static SignletonThree _instance;
    
            public static SignletonThree Instance
            {
                get
                {
                    if (_instance==null)
                    {
                        lock (objLock)
                        {
                            _instance = _instance ?? new SignletonThree();
                        }
                    }
                    return _instance;
                }
            }
        }
    

      很不错也很通用的写法!当_instance为null去创建实例的时候,进行加锁操作,加上了一个判断条件,即使是在多线程的情况下,也只会进行一次枷锁操作,效率上绝对比第二种高。是很好的实现模式,但还有其他写法:

        public sealed class SignletonFour
        {
            private SignletonFour() { }
    
            private static SignletonFour _instance = new SignletonFour();
    
            public static SignletonFour Instance => _instance;
        }
    

      估计有人要喷了,你tm会不会写代码???在c#语法中,静态构造函数可以保证只调用一次,就是利用这个特性来实现,代码没几行,哈哈,在调用静态构造函数的时候去获取实例,也很好,没错的。代码简洁。但是在c#中,调用静态构造函数不是由我们去控制的,而是当clr发现第一次使用这个类型的时候去自动调用,所以说 在这种写法中 当通过 SignletonFour.Instance 去获取实例的时候,有可能实例已经被创建了。比如说 我们在这个单例类中加一个静态函数,当我们调用这个静态函数的时候,本来是不需要创建这个实例的,但是按照这种写法呢,当我们调用静态函数的时候,这个实例也就被创建了。但是我们不需要,看下一种写法:

     public sealed class SignletonFive
        {
            private SignletonFive() { }
    
            public static SignletonFive Instance => Nested.Instance;
    
            private class Nested
            {
                static Nested() { }
                public static readonly SignletonFive Instance = new SignletonFive();
            }
        }

      在这种写法中,在内部定义了一个私有类型,当我们调用这个嵌套类型的时候,就会去调用私有构造函数去创建实例,而这个内部类型,也就只是会在 SignletonFive.Instance 的时候会用到。这时候再去调用自定义的静态方法,就不会出现过早创建实例的情况。

    单例模式有很多写法,其实也不必纠结用什么写法。适合的才是最好的。因为当我们在业务里面正儿八经去使用的时候,能很好的满足需求,以及保证高效的使用,就是最好的。

  • 相关阅读:
    Android 操作系统架构开篇
    《构建之法》读后感
    《梦断代码》读后感
    学习日报
    学习日报
    记账本开发4
    记账本开发3
    学习日报
    学习日报
    记账本开发2
  • 原文地址:https://www.cnblogs.com/ZyCoder/p/9221211.html
Copyright © 2011-2022 走看看