设计模式之单例
一、什么是单例模式
单例模式是实现在程序的生命周期中,某个对象,只有一个实例。
二、单例模式的实现
1、如何保证一个对象,只有一个实例?首先,我们必须要用到关键字“static”,以下是一个最简单的单例写法:
public class Singleton { //定义一个私有静态变量,用于存储唯一的实例对象。 private static Singleton instance = null; //私有化的构造函数,使的外界不能使用new创建新的实例 private Singleton() { Console.WriteLine("对象被构造了一次"); } /// <summary> /// 获取本类实例的唯一入口 /// </summary> /// <returns></returns> public static Singleton GetInstance() { //只有对象不存在的时候,才会实例化一个对象(初始访问的时候) if (instance == null) { instance = new Singleton(); } //返回实例 return instance; } }
2、测试获取到的对象是否相
//获取多个对象 Singleton singleton1 = Singleton.GetInstance(); Singleton singleton2 = Singleton.GetInstance(); Singleton singleton3 = Singleton.GetInstance(); //判断对象是否相同 Console.WriteLine("singleton1和singleton2比较结果:" + object.ReferenceEquals(singleton1, singleton2)); Console.WriteLine("singleton2和singleton3比较结果:" + object.ReferenceEquals(singleton2, singleton3)); Console.Read();
3、输出结果
4、现在的代码,在多线程的情况下,是否也能保持单个实例?
for (int i = 0; i < 5; i++) { Task.Run(() => { Singleton singleton = Singleton.GetInstance(); }); }
5、运行结果:
6、对象被重复构造了五次,说明生成了五个不同的实例,导致这种情况的原因是:第一个实例还没有构造完成,其它线程就进入代码构造实例了。解决方式:使用lock;
public class Singleton { //定义一个私有静态变量,用于存储唯一的实例对象。 private static Singleton instance = null; //程序运行时,创建一个只读的静态变量 private static readonly string str_lock = ""; //私有化的构造函数,使的外界不能使用new创建新的实例 private Singleton() { Console.WriteLine("对象被构造了一次"); } /// <summary> /// 获取本类实例的唯一入口 /// </summary> /// <returns></returns> public static Singleton GetInstance() { lock(str_lock) { //只有对象不存在的时候,才会实例化一个对象(初始访问的时候) if (instance == null) { instance = new Singleton(); } } //返回实例 return instance; } }
lock:官方定义:确保当一个线程位于代码临界区时,另一个线程不能进入临界区。如果其它线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放;通俗的讲,就是其它线程进入队列中,知道前面的线程执行完成,下一个线程才会进入被锁定的代码区域执行。
这里需要关注的一点是:多线程进入被锁的区域后,多线程就会变成同步执行。
7、代码到了这里,还有一个问题,就是不管这个对象是否已经被实例化,所有线程进来后,都要进入锁,变成同步执行,这样对多线程是有影响的,因为多线程最大的优势,就是异步执行,所以,我们还要在锁的外层,再加一次判断,判断对象是否为null;这是经典的写法:双重锁定。
public static Singleton GetInstance() { //避免对象实例化之后 if (instance == null) { lock (str_lock) { //只有对象不存在的时候,才会实例化一个对象(初始访问的时候) if (instance == null) { instance = new Singleton(); } } } //返回实例 return instance; }
这里存在两次判断instance为null,但是都不能被去掉,里边的一层判断被去掉的化,初始实例化对象,如果并发较高,又会出现多次实例化的情况。