zoukankan      html  css  js  c++  java
  • 设计模式学习-单例模式

    饿汉模式

    单例模式又被称为单件模式,这个模式作用是保持程序中只有`唯一`对象,一听到唯一,那肯定就明白了,无非就是不让别人创建新对象呗,只需要两点就可以
    

    1.私有化构造函数,

    2.创建一个静态对象属性以便外部使用

    这么简单还算一种模式?别急,我们慢慢看,首先,我们创建一个单例

    class Singleton
    {
        //静态对象属性
        public static Singleton SingletonInstance { get; } = new Singleton();
        //私有化构造函数
        private Singleton()
        {
            Console.WriteLine("Singleton构造");
        }
    }
    

    挺简单的吗这不是,的确,挺简单的,这就是单例模式其中之一的饿汉模式,什么意思呢,

    饿汉模式:在程序启动单例类被加载时,就实例化单例模式

    但是这么做不感觉有问题吗?假如这个类我们并不使用或在程序启动很久以后我们才使用,那么这个对象的预创建不就很浪费吗?并且如果这个对象的创建需要很大的资源,那....,所以我们需要延迟单例对象的创建.

    懒汉模式

    将对象延迟到第一次访问时才创建,这种被称为`懒汉模式`
    

    懒汉模式:当第一次访问单例对象时才去实例化对象

    看起来也挺简单的样子,无非是将对象实例化放在属性的get

    class Singleton
    {
        private static Singleton _singleton=null;	
        //静态对象属性
        public static Singleton SingletonInstance
        {
            get
            {
                if (_singleton == null)
                {//如果对象不存在则创建
                    _singleton = new Singleton();
                }
                return _singleton;
            }
    
        }
        //私有化构造函数
        private Singleton()
        {
            Console.WriteLine("Singleton构造");
        }
    }
    

    看起来感觉挺不错的样子,但是真是这样吗?来做一个多线程的例子看看.

    static void Main(string[] args)
    {
           Task.Run(() => { Singleton singleton1 = Singleton.SingletonInstance;});
           Task.Run(() => { Singleton singleton1 = Singleton.SingletonInstance;});
           Task.Run(() => { Singleton singleton1 = Singleton.SingletonInstance;});
           Task.Run(() => { Singleton singleton1 = Singleton.SingletonInstance;});
           Console.ReadKey();
     }
    

    开4个线程进行测试

    Task.Run() 是从线程池中获取线程进行使用

    1536140732087

    可以看到对于多线程来说这个单例完全无用,解决多线程的办法就是加锁,所以需要在实例化对象进行加锁

    private static object objLock = new object();
    //静态对象属性
    public static Singleton SingletonInstance
    {
         get
         {
             lock (objLock)
             {
                if (_singleton == null)
                 {//如果对象不存在则创建
                    _singleton = new Singleton();
                  }
              }
              return _singleton;
         }
    }
    

    但是都知道,加锁会极大的影响性能,每一次都判断锁感觉上是一种极大的浪费.,然后再次优化就出现了经典的单例实例实现双重检查锁

    public static Singleton SingletonInstance
    {
        get
        {
            if (_singleton == null)
            {//解决锁的效率问题.
                lock (objLock)
                {
                    if (_singleton == null)
                    {//如果对象不存在则创建
                    	_singleton = new Singleton();
                    }
                }
            }
            return _singleton;
        }
    }
    

    在外面加一个if判断,这层if看起来是多余的,但是它极大的节省了性能,杜绝了大多数无多并发时锁的验证,从而提高了性能.

    C#单例另一种实现---延迟加载

    在C#中有一个Lazy类,这个类是一个延迟加载类,也就是自动为我们实现延迟加载功能,并且还是线程安全的,也就是说完全可以利用这个类实现单例

    class SingletonLazy
    {
        //Lazy需要一个无参有返的委托,
        public static  Lazy<SingletonLazy> SingletonInstance = new Lazy<SingletonLazy>(() => { return new SingletonLazy(); });
        //私有化构造函数
        private SingletonLazy()
        {
        	Console.WriteLine("SingletonLazy构造");
        }
    }
    

    Lazy构造器有一个参数是传入一个无参有返的委托,整好可以实例化对象,

    static void Main(string[] args)
    {
        SingletonLazy singletonLazy = null;
        Console.WriteLine("延迟");
        Task.Run(() => { SingletonLazy singleton1 = SingletonLazy.SingletonInstance.Value; });
        Task.Run(() => { SingletonLazy singleton1 = SingletonLazy.SingletonInstance.Value; });
        Task.Run(() => { SingletonLazy singleton1 = SingletonLazy.SingletonInstance.Value; });
        Task.Run(() => { SingletonLazy singleton1 = SingletonLazy.SingletonInstance.Value; });
        Console.ReadKey();
    }
    

    1536236190027

    对这个单例进行测试,测试结果与刚才无异,在工作中很多都是使用这种方式来实现单例模式

    Lazy实现机制

    下面来看看Lazy的实现机制,其实我们也大致能想到内部到底是如何处理的

    public class Lazy<T>
    {
        //内部类,存储数据的类
        private class Boxed
        {
            internal T m_value;
    
            internal Boxed(T value)
            {
                m_value = value;
            }
        }
        private object m_boxed;
        //委托
        private Func<T> m_valueFactory;
        //线程安全对象
        private object m_threadSafeObj = new object();
        //委托默认值
        private static readonly Func<T> ALREADY_INVOKED_SENTINEL = () => default(T);
        public Lazy(Func<T> valueFactory)
        {
            m_valueFactory = valueFactory ?? throw new ArgumentNullException("valueFactory");
        }
        //Value
        public T Value
        {
            get
            {
                Boxed boxed = null;
                if (m_boxed != null)
                {
                    boxed = (m_boxed as Boxed);
                    if (boxed != null)
                    {
                        return boxed.m_value;
                    }
                    throw new Exception();
                }
                return LazyInitValue();
            }
        }
        //初始化Value值
        private T LazyInitValue()
        {
            Boxed boxed = null;
            //读取安全对象
            object obj = Volatile.Read<object>(ref m_threadSafeObj);
            bool flag = false;
            try
            {
                //如果是第一次,则开启锁并创建对象
                if (ALREADY_INVOKED_SENTINEL !=obj)
                { 
                    Monitor.Enter(obj, ref flag);
                }
                if (m_boxed == null)
                {
                    //创建Boxed对象并将m_threadSafeObj设置为ALREADY_INVOKED_SENTINEL
                    boxed = (Boxed)(m_boxed = CreateValue());
                    Volatile.Write<object>(ref m_threadSafeObj, (object)ALREADY_INVOKED_SENTINEL);
                }
                else
                {//已存在对象
                    boxed = (m_boxed as Boxed);
                }
            }
            finally
            {
            if (flag)
                {
                    Monitor.Exit(obj);
                }
            }
            return boxed.m_value;
        }
        //创建Boxed对象
        private Boxed CreateValue()
        {
            if (m_valueFactory != null)
            {
                try
                {
                    Func<T> valueFactory = m_valueFactory;
                     if (valueFactory == ALREADY_INVOKED_SENTINEL)
                    {
                        return null;
                    }
                    return new Boxed(valueFactory());
                }
                catch (Exception)
                {
                    throw;
                }
            }
            try
            {
                //创建默认对象
                return new Boxed((T)Activator.CreateInstance(typeof(T)));
            }
            catch (MissingMethodException)
            {
                throw;
            }
        }
    }
    

    上面是简化版的Lazy源码,可以看到Lazy也只是利用了一个内部类Boxed对象缓存了数据,代码中有一点有意思的,在LazyInitValue()方法中使用了Volatile类读取数据进行加锁,volatile是保持多线程下数据同步的问题,一种简单方式可以在变量中加上volatile关键字

    多个线程同时访问一个变量时,CLR(Common Language Runtime)为了效率会进行相应优化,比如“允许线程进行本地缓存”,这样就可能导致变量访问的不一致性。volatile就是为了解决这个问题;volatile修饰的变量,不允许线程进行本地缓存,每个线程的读写都直接操作在共享内存上,这就保证了变量始终具有一致性

    单例模式定义

    单例模式保证在系统中一个类仅有一个实例对象

  • 相关阅读:
    瓦力完成图
    树莓派学习笔记(6):让Raspbian支持中文、禁用休眠
    树莓派学习笔记(5):成功实现NAS家庭服务器(流媒体播放、文件共享及下载机)
    检测QQ在线状态脚本(20141022测试成功)
    树莓派学习笔记(4):利用yeelink实现在线硬件状态监控
    vi-vim :删除、撤销、恢复删除、复制删除
    sqlachemy中批量删除的问题
    vim 删除一整块,vim 删除一整行
    vim显示行号、语法高亮、自动缩进、添加下划线的设置
    CentOs6.7 python2.6升级到2.7.11
  • 原文地址:https://www.cnblogs.com/yan7/p/9794537.html
Copyright © 2011-2022 走看看