设计模式——单例模式
Posted 勤学才是王道
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式——单例模式相关的知识,希望对你有一定的参考价值。
确保一个类只有一个实例,并提供一个全局访问点
- 当类只能有一个实例,而且客户可以从一个众所周知的访问点访问它时。
- 当这个唯一的实例应该是通过子类化可扩展的,而且客户应该无需更改代码就能使用一个扩展的实例时。
如何确保外部无法使用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
以上是关于设计模式——单例模式的主要内容,如果未能解决你的问题,请参考以下文章