设计模式之单例模式

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

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

    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 的时候会用到。这时候再去调用自定义的静态方法,就不会出现过早创建实例的情况。

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

原文地址:https://www.cnblogs.com/ZyCoder/p/9221211.html