概述
单例模式大概是23种设计模式里面用的最多,也用的最普遍的了,也是很多很多人一问设计模式都有哪些必答的第一种了;我们先复习一下饿汉式和懒汉式的单例模式,再谈其创建方式会带来什么问题,并一一解决!还是老规矩,先上代码,不上代码,纸上谈兵咱把握不住。.
饿汉式代码
public class SingleHungry{private readonly static SingleHungry _singleHungry = new SingleHungry();private SingleHungry(){}public static SingleHungry GetSingleHungry(){return _singleHungry;}}
第一种测试:构造函数私有的,new的时候报错,因为我们的构造函数是私有的。
SingleHungry _singleHungry=new SingleHungry();第二种测试:比对创建多个对象,然后多个对象的Hashvalue
public class SingleHungryTest{public static void FactTestHashCodeIsSame(){Console.WriteLine("单例模式.饿汉式测试!");var single1 = SingleHungry.GetSingleHungry();var single2 = SingleHungry.GetSingleHungry();var single3 = SingleHungry.GetSingleHungry();Console.WriteLine(single1.GetHashCode());Console.WriteLine(single2.GetHashCode());Console.WriteLine(single3.GetHashCode());}}
测试下来,三个对象的hash值是一样的。如下图:

饿汉式结论总结
饿汉式的单例模式不推荐使用,因为还没调用,对象就已经创建,造成资源的浪费;
懒汉式代码
public class SingleLayMan{//1、私有化构造函数private SingleLayMan(){}//2、声明静态字段 存储我们唯一的对象实例private static SingleLayMan _singleLayMan;//通过方法 创建实例并返回public static SingleLayMan GetSingleLayMan1(){//这种方式不可用 会创建多个对象,谨记return _singleLayMan = new SingleLayMan();}/// <summary>///懒汉式单例模式只有在调用方法时才会去创建,不会造成资源的浪费/// </summary>/// <returns></returns>public static SingleLayMan GetSingleLayMan2(){if (_singleLayMan == null){Console.WriteLine("我被创建了一次!");_singleLayMan = new SingleLayMan();}return _singleLayMan;}}
测试代码
public class SingleLayManTest{/// <summary>/// 会创建多个对象.hash值不一样/// </summary>public static void FactTest(){Console.WriteLine("单例模式.懒汉式测试!");var singleLayMan1 = SingleLayMan.GetSingleLayMan1();var singleLayMan2 = SingleLayMan.GetSingleLayMan1();Console.WriteLine(singleLayMan1.GetHashCode());Console.WriteLine(singleLayMan2.GetHashCode());}/// <summary>/// 单例模式.懒汉式测试:懒汉式单例模式只有在调用方法时才会去创建,不会造成资源的浪费,但会有线程安全问题/// </summary>public static void FactTest1(){Console.WriteLine("单例模式.懒汉式测试!");var singleLayMan1 = SingleLayMan.GetSingleLayMan2();var singleLayMan2 = SingleLayMan.GetSingleLayMan2();Console.WriteLine(singleLayMan1.GetHashCode());Console.WriteLine(singleLayMan2.GetHashCode());}/// <summary>/// 单例模式.懒汉式多线程环境测试!/// </summary>public static void FactTest2(){Console.WriteLine("单例模式.懒汉式多线程环境测试!");for (int i = 0; i < 10; i++){new Thread(() =>{SingleLayMan.GetSingleLayMan2();}).Start();}//Parallel.For(0, 10, d => {// SingleLayMan.GetSingleLayMan2();//});}}
懒汉式结论总结
其它方式创建单例—饿汉式+静态内部类
public class SingleHungry2{public static SingleHungry2 GetSingleHungry(){return InnerClass._singleHungry;}public static class InnerClass{public readonly static SingleHungry2 _singleHungry = new SingleHungry2();}}
这个代码,用了饿汉式结合静态内部类来创建单例,线程也安全,不失为创建单例的一种办法。
其它方式创建单例—懒汉式+反射
首先我们解决一下刚才懒汉式创建单例的线程安全问题,上代码:
/// <summary>/// 通过反射破坏创建对象/// </summary>public class SingleLayMan1{//私有化构造函数private SingleLayMan1(){}//2、声明静态字段 存储我们唯一的对象实例private static SingleLayMan1? _singleLayMan;private static object _oj = new object(); /// <summary>/// //解决多线程安全问题,双重锁定,减少系统消耗,节约资源/// </summary>public static SingleLayMan1 GetSingleLayMan(){if (_singleLayMan == null){lock (_oj){if (_singleLayMan == null){_singleLayMan = new SingleLayMan1();Console.WriteLine("我被创建了一次!");}}}return _singleLayMan;}}
public class SingleLayManTest1{public static void FactTestReflection(){var singleLayMan1= SingleLayMan1.GetSingleLayMan();var type = Type.GetType("_01单例模式.反射破坏单例模式.SingleLayMan1");//获取私有的构造函数var ctors = type?.GetConstructors(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);//执行构造函数SingleLayMan1 singleLayMan = (SingleLayMan1)ctors[0].Invoke(null);Console.WriteLine(singleLayMan1.GetHashCode());Console.WriteLine(singleLayMan.GetHashCode());}}
2、 执行构造函数 创建对象
3、 把空间指向我们的对像
private volatile static SingleLayMan? _singleLayMan;/// <summary>/// 解决反射创建对象的问题/// </summary>public class SingleLayMan3{//2、声明静态字段 存储我们唯一的对象实例private volatile static SingleLayMan3? _singleLayMan;private static object _oj = new object();//私有化构造函数private SingleLayMan3(){lock (_oj){if (_singleLayMan != null){throw new Exception("不要通过反射来创建对像!");}}}/// <summary>/// //解决多线程安全问题,双重锁定,减少系统消耗,节约资源/// </summary>public static SingleLayMan3 GetSingleLayMan(){if (_singleLayMan == null){lock (_oj){if (_singleLayMan == null){_singleLayMan = new SingleLayMan3();Console.WriteLine("我被创建了一次!");}}}return _singleLayMan;}}
public class SingleLayManTest3{/// <summary>/// 第一次通过调用 SingleLayMan3.GetSingleLayMan()创建对象导致_singleLayMan不为空,之后再去通过反射创建对象时,构造函数里面判断创建对象导致_singleLayMan变量,报异常/// </summary>public static void FactTestReflection(){var singleLayMan1= SingleLayMan3.GetSingleLayMan();var type = Type.GetType("_01单例模式.反射破坏单例模式.SingleLayMan3");//获取私有的构造函数var ctors = type?.GetConstructors(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);//执行构造函数SingleLayMan3 singleLayMan = (SingleLayMan3)ctors[0].Invoke(null);Console.WriteLine(singleLayMan1.GetHashCode());Console.WriteLine(singleLayMan.GetHashCode());}}
定义局部变量解决反射创建对象问题
public class SingleLayMan4{//2、声明静态字段 存储我们唯一的对象实例private volatile static SingleLayMan4? _singleLayMan;private static object _oj = new object();private static bool _isOk = false;//私有化构造函数private SingleLayMan4(){lock (_oj){if (_isOk == false){_isOk = true;}else{throw new Exception("不要通过反射来创建对像!只有第一次通过反射创建对象会成功!请做第一个吃葡萄的人!");}}}/// <summary>/// //解决多线程安全问题,双重锁定,减少系统消耗,节约资源/// </summary>public static SingleLayMan4 GetSingleLayMan(){if (_singleLayMan == null){lock (_oj){if (_singleLayMan == null){_singleLayMan = new SingleLayMan4();Console.WriteLine("我被创建了一次!");}}}return _singleLayMan;}}
public static void FactTestReflection(){//第一次创建对象会成功var singleLayMan1 = GetReflectionSingleLayMan4Instance();//第二次创建对象会失败,报异常var singleLayMan2 = GetReflectionSingleLayMan4Instance();Console.WriteLine(singleLayMan1.GetHashCode());}private static SingleLayMan4 GetReflectionSingleLayMan4Instance(){var type = Type.GetType("_01单例模式.反射破坏单例模式.SingleLayMan4");//获取私有的构造函数var ctors = type?.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);//执行构造函数SingleLayMan4 singleLayMan = (SingleLayMan4)ctors[0].Invoke(null);return singleLayMan;}
/// <summary>/// 通过反射也可以改变局部变量_isOk的值,继续创建对象/// </summary>public static void FactTestReflection2(){Type type = Type.GetType("_01单例模式.反射破坏单例模式.SingleLayMan4");//获取私有的构造函数var ctors = type?.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);//执行构造函数SingleLayMan4 singleLayMan1 = (SingleLayMan4)ctors[0].Invoke(null);FieldInfo fieldInfo = type.GetField("_isOk", BindingFlags.NonPublic | BindingFlags.Static);fieldInfo.SetValue("_isOk", false);SingleLayMan4 singleLayMan2 = (SingleLayMan4)ctors[0].Invoke(null);Console.WriteLine(singleLayMan1.GetHashCode());Console.WriteLine(singleLayMan2.GetHashCode());}