设计模式之单例模式
有点编程经验的人应该都知道单例模式,属于创建型模式,定义也挺简单,一个类有且仅有一个实例,并且提供一个全局访问点。
根据定义写一个单例类挺简单的,比如这样子:
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 的时候会用到。这时候再去调用自定义的静态方法,就不会出现过早创建实例的情况。
单例模式有很多写法,其实也不必纠结用什么写法。适合的才是最好的。因为当我们在业务里面正儿八经去使用的时候,能很好的满足需求,以及保证高效的使用,就是最好的。