设计模式——单例模式

确保一个类只有一个实例,并提供一个全局访问点

  • 当类只能有一个实例,而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一的实例应该是通过子类化可扩展的,而且客户应该无需更改代码就能使用一个扩展的实例时。

如何确保外部无法使用new来创建类的实例?在此类中将构造函数设为 private 

 1  /// <summary>
 2     /// 单例模式的实现
 3     /// </summary>
 4     public class Singleton
 5     {
 6         // 定义一个静态变量来保存类的实例
 7         private static Singleton uniqueInstance;
 8 
 9         // 定义私有构造函数,使外界不能创建该类实例
10         private Singleton()
11         {
12         }
13 
14         /// <summary>
15         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
16         /// </summary>
17         /// <returns></returns>
18         public static Singleton GetInstance()
19         {
20             // 如果类的实例不存在则创建,否则直接返回
21             if (uniqueInstance == null)
22             {
23                 uniqueInstance = new Singleton();
24             }
25             return uniqueInstance;
26         }
27     }

在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了。

 1 /// <summary>
 2     /// 单例模式的实现
 3     /// </summary>
 4     public class Singleton
 5     {
 6         // 定义一个静态变量来保存类的实例
 7         private static Singleton uniqueInstance;
 8 
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11 
12         // 定义私有构造函数,使外界不能创建该类实例
13         private Singleton()
14         {
15         }
16 
17         /// <summary>
18         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19         /// </summary>
20         /// <returns></returns>
21         public static Singleton GetInstance()
22         {
23             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26             lock (locker)
27             {
28                 // 如果类的实例不存在则创建,否则直接返回
29                 if (uniqueInstance == null)
30                 {
31                     uniqueInstance = new Singleton();
32                 }
33             }
34 
35             return uniqueInstance;
36         }
37     }

上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”

 1 /// <summary>
 2     /// 单例模式的实现
 3     /// </summary>
 4     public class Singleton
 5     {
 6         // 定义一个静态变量来保存类的实例
 7         private static Singleton uniqueInstance;
 8 
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11 
12         // 定义私有构造函数,使外界不能创建该类实例
13         private Singleton()
14         {
15         }
16 
17         /// <summary>
18         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19         /// </summary>
20         /// <returns></returns>
21         public static Singleton GetInstance()
22         {
23             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26             // 双重锁定只需要一句判断就可以了
27             if (uniqueInstance == null)
28             {
29                 lock (locker)
30                 {
31                     // 如果类的实例不存在则创建,否则直接返回
32                     if (uniqueInstance == null)
33                     {
34                         uniqueInstance = new Singleton();
35                     }
36                 }
37             }
38             return uniqueInstance;
39         }
40     }

.NET FrameWork类库中有没有单例模式的实现呢?

.NET类库中确实存在单例模式的实现类,不过该类不是公开的,下面就具体看看该类的一个实现的(该类具体存在于System.dll程序集,命名空间为System,大家可以用反射工具Reflector去查看源码的

 1 // 该类不是一个公开类
 2     // 但是该类的实现应用了单例模式
 3     internal sealed class SR
 4     {
 5         private static SR loader;
 6         internal SR()
 7         {
 8         }
 9         // 主要是因为该类不是公有,所以这个全部访问点也定义为私有的了
10         // 但是思想还是用到了单例模式的思想的
11         private static SR GetLoader()
12         {
13             if (loader == null)
14             {
15                 SR sr = new SR();
16                 Interlocked.CompareExchange<SR>(ref loader, sr, null);
17             }
18             return loader;
19         }
20 
21         // 这个公有方法中调用了GetLoader方法的
22         public static object GetObject(string name)
23         {
24             SR loader = GetLoader();
25             if (loader == null)
26             {
27                 return null;
28             }
29             return loader.resources.GetObject(name, Culture);
30         }
31     }

学习于  https://www.cnblogs.com/zhili/p/SingletonPatterm.html

原文地址:https://www.cnblogs.com/cwmizlp/p/9141366.html