设计模式之单例

一、什么是单例模式

        单例模式是实现在程序的生命周期中,某个对象,只有一个实例。

二、单例模式的实现

       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;
        }
    }
View Code

       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();
View Code

  3、输出结果

  

  4、现在的代码,在多线程的情况下,是否也能保持单个实例?

for (int i = 0; i < 5; i++)
                {
                    Task.Run(() => {
                        Singleton singleton = Singleton.GetInstance();
                    });
                }
View Code

  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;
        }
    }
View Code

  lock:官方定义:确保当一个线程位于代码临界区时,另一个线程不能进入临界区。如果其它线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放;通俗的讲,就是其它线程进入队列中,知道前面的线程执行完成,下一个线程才会进入被锁定的代码区域执行。

  这里需要关注的一点是:多线程进入被锁的区域后,多线程就会变成同步执行。

  7、代码到了这里,还有一个问题,就是不管这个对象是否已经被实例化,所有线程进来后,都要进入锁,变成同步执行,这样对多线程是有影响的,因为多线程最大的优势,就是异步执行,所以,我们还要在锁的外层,再加一次判断,判断对象是否为null;这是经典的写法:双重锁定。

  

public static Singleton GetInstance()
        {
            //避免对象实例化之后
            if (instance == null)
            {
                lock (str_lock)
                {
                    //只有对象不存在的时候,才会实例化一个对象(初始访问的时候)
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            //返回实例
            return instance;
        }

  这里存在两次判断instance为null,但是都不能被去掉,里边的一层判断被去掉的化,初始实例化对象,如果并发较高,又会出现多次实例化的情况。

posted @ 2023-03-22 14:15  资深码农0311  阅读(39)  评论(0编辑  收藏  举报