首页 > 代码库 > 单例模式
单例模式
什么是单例模式?
简单来说就是要创建一个独一无二的对象。
确保一个类只有一个实例,并提供一个全局的访问点。
有哪些使用场景?
- 线程池
- 缓存
- 注册表
- 充当打印机、显卡等设备的驱动程序的对象
剖析经典的单件模式的实现
1 public class Singleton 2 { 3 private static Singleton singleton; 4 private Singleton() 5 { 6 } 7 public static Singleton GetInstance() 8 { 9 if (null == singleton)10 {11 singleton = new Singleton ();12 }13 return singleton;14 }15 }
1.利用静态变量来记录唯一的实例
2.把构造器声明为私有的,只有singleton类内部才可以调用构造器
3.用getInstance()实例化对象,并返回这个实例。
4.null==singleton是空的表示没有创建实例,如果它不存在我们就可以利用私有构造器产生一个singleton的实例并把它赋值到静态变量中。请注意如果我们不需要这个实例,他就永远不会产生。这就是“延迟实例化”
5.如果不是null,就说明已经有了实例,我们直接跳到return语句。
单件模式的类图
单件模式的序列图
Client调用
1 public class Client 2 { 3 static void Main(string[] args) 4 { 5 Singleton s1 = Singleton .GetInstance(); 6 7 Singleton s2 = Singleton .GetInstance(); 8 9 Console.WriteLine(s1 == s2);10 11 Console.ReadKey();12 }13 }
输出结果: True
client调用结果分析
1.从上面的结果可以看出尽管两次访问了GetInstance(),但是访问的只是同一个实例。
2.由于构造函数设为私有的,类外部无法使用new实例化一个实例,只能通过getInstance()创建一个实例,如果存在则不会创建实例,而是调用以前生成的实例。
分析经典单例模式存在的问题
如果多个线程同时调用呢?我们举个例子,例如有线程A和B两个线程,A线程进入if判断语句后还没实例化,B线程到达,此时singleton还是为null,这样的话就让两个线程均通过if语句判断,然后调用new singleton()了,这样就创建了两个实例,很显然就违反了单例模式的初衷了。
如何解决多线程的问题?
方法一:双重检查锁定
1 public class Singleton 2 { 3 private static Singleton singleton; 4 private static readonly object syncObject = new object(); 5 private Singleton() { } 6 public static Singleton GetInstance() 7 { 8 if (singleton == null) 9 { 10 lock (syncObject) 11 { 12 if (singleton == null) 13 { 14 singleton = new Singleton(); 15 } 16 } 17 } 18 return singleton; 19 } 20 }
1、例如有两个线程A和B,当线程A到达lock下面的if语句时,线程B也到到达lock语句,此时线程B不会往下执行,因为已经锁定,需要等线程B执行完lock语句块,才能往下走。
2、可以看到上面的代码有两个if(singleton==null),第一个其实是基于性能的考虑,如果没有第一个if(single==null),那每次调用这段代码时,当有多个线程时都会造成线程阻塞。而加了以后,如果已经创建了对象,当调用到第一个if(single==null)时,就会跳到return语句,不会执行lock代码。保证了性能最优。
3、第二个if(singleton==null)保证了多线程的环境下只创建一个对象。
其中需要注意lock的用法
1.lock关键字的参数必须为引用类型,如果是值类型,会报编译错误。
2.锁定的类型必须是安全的,所以是private的,如果是public的话可能其他地方也会锁定它,可能会导致死锁。
方法二:饿汉式单例
1 namespace Singleton { 2 public class Singleton 3 { 4 private static readonly Singleton singleton = new Singleton(); 5 private Singleton() { } 6 public static Singleton GetInstance() 7 { 8 return singleton; 9 } 10 } 11 }
1、当程序一启动时就创建了一个对象,并用静态变量来接收它。
2、当调用GetInstance()方法时直接返回静态变量。
3、在整个程序中只有一次创建对象,就是程序启动时。所以在多线程使用中不会产生问题。
4、在程序一启动时就已经创建了对象,也就提前占用了资源,当对提前占用资源不关心的话,可以使用此方法。
总结:
- 单例模式使类在程序生命周期的任何时刻都只有一个实例
- 单例的构造函数是私有的
- 必须通过 GetInstance()来请求得到这个单例类的实例。
单例模式