单例模式是设计模式中最简单模式了,主要作用是对于一个类,在整个项目中只实例化一次,本次记录四种实现方式,并说说优劣性。
1.懒汉模式实现:
class Single1 { //懒汉模式顾名思义,就是比较懒,在类加载的时候不调用实例方法 //当真正调用的时候去实例化方法 private static Single1 SingleObject; private Single1() { } public static Single1 getSingle1() { if (SingleObject == null) { SingleObject = new Single1(); } return SingleObject; } }
私有构造函数目的是为了不让使用new初始化
缺点:在多线程情况下,都在访问这个类,可能在两个线程或多个线程同时进入single=null(因为其中还未来的及进行实例化),所以线程不安全。
优点:它实例化是在真正调用这个静态方法的时候去加载内存中,而不是项目启动就加载内存中,这样对于项目启动减小负担。
本想使用多线程测试一下懒汉模式缺点,但发现使用GCHandle.Alloc();获取地址的值每次发生变,网上查了下,发现C#不支持地址获取,并且这个值每次发生变化。
网上发现这个blog实现了懒汉的缺点:https://www.cnblogs.com/zh7791/p/7930342.html
2.饿汉模式实现:
class Single2 { //使用饿汉模式,意思就是在项目加载的时候,实例就创建了 private static Single2 single2 = new Single2(); private Single2() { } public static Single2 getSingle2() { return single2; } }
缺点:就是类加载的时候就创建了,这里就比较慢,对于第一次加载;
优点:这里线程是安全的,每次进来调用就是第一次类加载出来的那个。
上面两种方法都是很好的方法,对于一个单例模式来说:
为了达到线程安全又不第一次就加载的情况可以在懒汉模式上加锁判断,这就能对线程不安全的代码在调用的是后变成串行的。
一般都是加双重判断,这是为了在下次调用先判断,这样就不用了下次加锁判断了,对于运行速度所有提升。
3.加锁双重判断懒汉模式进化版:
class Single3 { //带锁的两重判断能将不安全的线程调用变成安全线程 private static Single3 single3; private static Object Locked=new Object(); private Single3() { } public static Single3 GetSingle3() { if (single3 != null) { lock (Locked) { if( single3 != null) { single3 = new Single3(); } } } return single3; } }
优点:这里使用带锁的双重判断可以有了懒汉模式的优点,并且除去他的缺点,是一个较好的单例实现方法。
4.静态内部类:
class Single4 { //静态内部类 private Single4() { } public static Single4 GetSingle4() { return GetSingle4Inside.single4; } private class GetSingle4Inside { internal static readonly Single4 single4 = new Single4(); } }
优点:这是使用了套娃模式:①上面静态方法会在第一次加载,但是不会调用里面的return,真正调用的时候才返回return内容,这样就不会再项目加载时实例化单例模式。
②这里使用了静态变量的特性,在内存加载的时候只实例化一次,所以他只实例化一次。
静态内部类也算比较好的一种实现方式。
5.C#特有的使用Lazy
class Single5 { //使用lazy实现单例 //里面参数是个func委托,返回的是T,无参 private static Lazy<Single5> single5 = new Lazy<Single5>(() => new Single5() ); private Single5() { } public Single5 GetSingle5() { return single5.Value; } }
优点:方法比起前面的更加优雅,不饶,而且写的更快
最后说以下单例模式适用地点:1.在一个不需要重复new的地方,比如配置文件加载。
2.对于资源需要反复利用,但是不需要重新实例化的地方,如线程池。