前言
单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。
一、简单模式
1 public class Singleton1
2 {
3 private static Singleton1 instance;
4 //构造私有,不能创建实例
5 private Singleton1()
6 {
7
8 }
9 //获得笨类实例的唯一全局访问点
10 public static Singleton1 GetInstance()
11 {
12 if (instance == null)
13 {
14 instance = new Singleton1();
15 }
16 return instance;
17 }
18 }
二、多线程保护模式
lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),知道该对象被释放。
1 //多线程时的单例
2 public class Singleton2
3 {
4 private static Singleton2 instance;
5 //程序运行时创建一个静态只读的进程辅助对象
6 private static readonly object syncRoot = new object();
7 //构造私有,不能创建实例
8 private Singleton2()
9 {
10
11 }
12 //获得笨类实例的唯一全局访问点
13 public static Singleton2 GetInstance()
14 {
15 //在同一时刻加了锁的那部分程序只有一个线程可以进入
16 lock (syncRoot)
17 {
18 if (instance == null)
19 {
20 instance = new Singleton2();
21 }
22 }
23 return instance;
24 }
25 }
这样每次调用GetInstance方法时都需要lock,会影响性能,所以有了下面的双重锁。
三、双重锁
当instance为null并且同时有两个线程调用GetInstance()方法时,他们讲都可以通过第一重instance == null的判断。然后由于lock机制,这两个线程则只有一个进入,另一个在lock那里排队等候,必须要其中的一个进入并出来后,另外一个才能进入。
如果没有第二重instance == null的判断,则第一个线程创建了实例,而第二个线程还可以继续创建实例。
1 public class Singleton3
2 {
3 private static Singleton3 instance;
4 //程序运行时创建一个静态只读的进程辅助对象
5 private static readonly object syncRoot = new object();
6 //构造私有,不能创建实例
7 private Singleton3()
8 {
9
10 }
11 //获得笨类实例的唯一全局访问点
12 public static Singleton3 GetInstance()
13 {
14 //先判断实例是否存在,不存在再加锁处理
15 if (instance == null)
16 {
17 //在同一时刻加了锁的那部分程序只有一个线程可以进入
18 lock (syncRoot)
19 {
20 if (instance == null)
21 {
22 instance = new Singleton3();
23 }
24 }
25 }
26 return instance;
27 }
28 }
四、静态初始化
C#与公共语言运行库提供了一种 ‘静态初始化’ 方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。
1 //密封类不能被继承
2 public sealed class Singleton4
3 {
4 //在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化
5 private static readonly Singleton4 instance = new Singleton4();
6
7 private Singleton4() { }
8 public static Singleton4 GetInstance()
9 {
10 return instance;
11 }
12 }
这种静态初始化的方式在自己被夹在时就将自己实例化,所以被形象地称为饿汉式单例类。
原来的单例 模式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。
总结
参考数据:大话设计模式