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 .......