Singleton(单例模式)
单例分两种,一种为饿汉式,而另一种则为懒汉式。
这两种之间的区别在于,他们产生的方式和时间不同,具体的内容,可以根据以下内容进行细分:
饿汉式单例
我们都知道,单例的意思指的是,只允许在一个进程中,仅有一个实例,那按照C#的语法,其实只需要通过static关键字进行修饰,即可完成这种需求。
1 ///2 /// 饿汉式单例 3 /// 4 public class EagerSingleton 5 { 6 /* 7 * 饿汉式单例构建分为以下几步 8 */ 9 10 /* 第一步,声明一个只读的私有字段,用来存放实例 */ 11 private static readonly EagerSingleton _instance; 12 13 /* 第二步,构造函数静态化,保证构造函数只会被执行一次;初始换 _instance 字段 */ 14 static EagerSingleton() => _instance = new EagerSingleton(); 15 16 /* 第三步,对外开放一个属性,用来向外界提供实例 */ 17 public static EagerSingleton GetInstance => _instance; 18 19 20 /// 21 /// 饿汉模式下的普通方法 22 /// 23 public void Show() => Console.WriteLine("我好饿,程序一运行,我就存在了"); 24 }
受static关键字修饰后,程序在编译运行的时候,系统会为我们自动执行构造方法,是不是感觉这家伙好像确实是有点迫不及待??,像个饿汉似的。假如我们的程序从始至终都没用到过这个实例,那岂不是说,他现在对我们没用,但是却一直占据着我们的内存空间,所以,正是因为有这种问题的产生,所以诞生了一个懒汉式单例。
懒汉式单例
懒汉式单例,相对而言,写起来就没这么简单了,以下代码是通过属性器的方式构建的单例:
1 ///2 /// 懒汉式单例 3 /// 4 public class LazySingleton 5 { 6 /* 7 * 关于懒汉式单例,分为以下几步 8 */ 9 10 /* 第一步,将构造函数进行私有化 */ 11 private LazySingleton() { } //这样,外界就不弄通过new的方式去创建实例 12 13 /* 第二步,开放一个属性出去,如果外界想要获得实例,则通过该属性去取得 */ 14 private static LazySingleton _instance; // 用于存放实例的字段 15 private static readonly object _obj = new object(); // 锁 16 public static LazySingleton GetInstance 17 { 18 get 19 { 20 if (_instance == null) 21 lock (_obj) 22 if (_instance == null) // 双重检查锁 23 _instance = new LazySingleton(); 24 return _instance; 25 } 26 } 27 28 29 /// 30 /// 单例中的一个普通的方法 31 /// 32 public void Show() => Console.WriteLine("我是一个小懒汉,你要是不主动点,我就不存在"); 33 }
当然,上面这种方式,是通过属性的方式,去获得实例的,关于这个,也可以把属性改成方法,这都是无碍的。
其实上面的代码中,主要的细节在于,在创建实例时的双重检查锁,关于这个,其实你可能会疑惑,为什么在lock之后,还要再加一层if判断。
可以这样想,假设好几个线程同时执行了第一个if判断,然后发现,目前的实例还未创建,然后这个时候,自然下一步就是去创建实例了,这个时候因为lock锁的原因,这几个线程都被堵在外面,必须一个一个线程继续执行lock的那一段代码。这第一个闯进去的线程,进去之后创建好了实例,然后走了,下一个线程再进来的时候,如果我们没有这一句if判断的话,你觉得会发生什么?没错,lock外面排队的几个线程全部都挨个儿进去创建实例,这严重违背了我们的初衷,一个进程中就这样被创建了好几个实例。
最终测试
最后,我们在控制台调用一下这两个单例。
1 static void Main(string[] args) 2 { 3 LazySingleton.GetInstance.Show(); // 调用懒汉式单例 4 EagerSingleton.Show(); // 调用饿汉式单例 5 6 Console.ReadKey(); 7 }
ending .......