zoukankan      html  css  js  c++  java
  • 设计模式 1/23 单例模式

    为什么将单例模式排名第一,很简单,面试的时候聊到设计模式,大概率就从单例模式开始入手,循循渐进。

    当然,我们学习单例模式不是为了去应付面试,而是学好设计模式后,融汇贯通,应用于我们的设计,开发,项目中。

    单例模式是最简单的设计模式之一

    单例模式【Singleton Pattern】:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    两层含义:1,有且仅有一个实例

         2,有一个访问他的全局访问点

    我们来想象一个场景,放眼全球,所有的党派,都只有一个主席(这应该没有例外吧)这个案例,如果要拜访他,是不是需要一个全局的访问点。

    对于一个类来说,我们怎样保证他仅有一个实例,初步想到的是不准外部实例化,因为如果可以外部实例化,我就可以实例化无数个。那不准外部实例化,就只能内部了,那就动手修改内部的构造方法。

    (敲黑板,划重点) 所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效

    如此,我们只需要显示的修改构造方法即可,既然不准外部调用,那我们用private修饰构造方法即可

    第一步,控制外部实例化我们就做到了

        /// <summary>
        /// 党派
        /// </summary>
        public class Party
        {
            /// <summary>
            /// 私有化构造函数,不准外部实例化
            /// </summary>
            private Party()
            {
            }
        }
    }
    View Code

    那我们需要一个主席,一个党派总不能群龙无首吧,于是我们定义一个主席,并且我们要提供一个访问的点吧,不然别人想和主席聊两句都找不到人

    第二步,创建一个主席,并提供一个访问点

        /// <summary>
        /// 党派
        /// </summary>
        public class Party
        {
            /// <summary>
            /// 我是党派主席
            /// </summary>
            private static Party _chairman;
    
            /// <summary>
            /// 私有化构造函数,不准外部实例化
            /// </summary>
            private Party()
            {
            }
    
            /// <summary>
            /// 找主席
            /// </summary>
            /// <returns></returns>
            public static Party GetChairman()
            {
                if (_chairman == null)//主席不存在则创建一个主席
                {
                    _chairman = new Party();
                }
                return _chairman;
            }
        }
    }
    View Code

    哇哇哇~!!!私有构造函数,为什么这里可以调用啊,不是不让调用么!!!这是同一个类中,同一个类中,私有,公有,保护,都可以随意调用。

    我们再让主席讲一句话

        /// <summary>
        /// 党派
        /// </summary>
        public class Party
        {
            /// <summary>
            /// 我是党派主席
            /// </summary>
            private static Party _chairman;
    
            /// <summary>
            /// 私有化构造函数,不准外部实例化
            /// </summary>
            private Party()
            {
            }
    
            /// <summary>
            /// 找主席
            /// </summary>
            /// <returns></returns>
            public static Party GetChairman()
            {
                if (_chairman == null)//主席不存在则创建一个主席
                {
                    _chairman = new Party();
                }
                return _chairman;
            }
    
            public void Say()
            {
                Console.WriteLine("同志们好!");
            }
        }
    View Code

    这样我们的一个简单的单单例模式就算完成了,我们再看看如何调用

     class Program
        {
            static void Main(string[] args)
            {
                Party chairman = Party.GetChairman();
                chairman.Say();
            }
        }
    View Code

    那么问题来了,单线程操作的时候,以上没有问题,那多线程的时候呢,多个人同时访问主席,会不会可能产生多个主席呢,多个主席,是不是就违反了单例模式最基本的原则,仅有一个实例呢。多个主席,难道打一架,胜者为王么?

    于是我们需要再次优化我们的代码

        /// <summary>
        /// 党派
        /// </summary>
        public class Party
        {
            /// <summary>
            /// 我是党派主席
            /// </summary>
            private static Party _chairman;
    
            /// <summary>
            /// 静态只读的进程辅助对象
            /// </summary>
            private static readonly object syncRoot = new object();
    
            /// <summary>
            /// 私有化构造函数,不准外部实例化
            /// </summary>
            private Party()
            {
            }
    
            /// <summary>
            /// 找主席
            /// </summary>
            /// <returns></returns>
            public static Party GetChairman()
            {
                lock (syncRoot)//锁你丫的
                {
                    if (_chairman == null)//主席不存在则创建一个主席
                    {
                        _chairman = new Party();
                    }
                }
                return _chairman;
            }
    
            public void Say()
            {
                Console.WriteLine("同志们好!");
            }
        }
    }
    View Code

    以上,再同一个时刻加了锁的那部分代码,只有一个线程可以进入。

    如果lock不懂,我知道你们懒

    lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放

    那么问题又来了,为什么每一次都要锁,为什么,见一次主席那么那么难么?能不能没有创建的时候我才锁,有主席的时候我直接访问啊

    当然可以,这就是双重锁定

        /// <summary>
        /// 党派
        /// </summary>
        public class Party
        {
            /// <summary>
            /// 我是党派主席
            /// </summary>
            private static Party _chairman;
    
            /// <summary>
            /// 静态只读的进程辅助对象
            /// </summary>
            private static readonly object syncRoot = new object();
    
            /// <summary>
            /// 私有化构造函数,不准外部实例化
            /// </summary>
            private Party()
            {
            }
    
            /// <summary>
            /// 找主席
            /// </summary>
            /// <returns></returns>
            public static Party GetChairman()
            {
                if (_chairman == null) //主席不存在,我再锁定,进行选举
                {
                    lock (syncRoot) //锁你丫的
                    {
                        if (_chairman == null) //主席不存在则创建一个主席
                        {
                            _chairman = new Party();
                        }
                    }
                }
                return _chairman;
            }
    
            public void Say()
            {
                Console.WriteLine("同志们好!");
            }
        }
    }
    View Code

    这里我们就先判断了主席是否存在,不存在,我们再闭门选举,等我们选举出来了,你再来访问。

    我们既优化了执行,也保证了仅有一个主席,保证了多线程访问的安全性。

    其实还有什么饿汉式,懒汉式的区别

    从字面理解

    饿汉,一开始我就实例化自己

            /// <summary>
            /// 我是党派主席
            /// </summary>
            private static Party _chairman = new Party();

    懒汉,被引用时候,才实例化自己

            /// <summary>
            /// 我是党派主席
            /// </summary>
            private static Party _chairman ;
    View Code

    具体使用哪一种,应该根据实际情况来定,无优劣之分,值得注意的是线程安全


    总结下

    优点:

    1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。

    2、避免对资源的多重占用(比如写文件操作)。

    缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

    使用场景: 

    1、要求生产唯一序列号。

    2、计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

    3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

    以上就是关于单例模式的分享

    一路前行,风雨无阻

  • 相关阅读:
    HLJU 1221: 高考签到题 (三分求极值)
    [视频解说]Java(JDK的下载安装及第一个程序执行)
    llvm中如何利用分支概率和基本块频率估计
    Nginx SSL TLS部署最佳实践
    Nginx使用教程(一):Nginx编译参数详解
    Nginx使用教程(一):下载并编译安装Nginx
    nginx使用ssl模块配置HTTPS支持
    Linux下安装Tomcat服务器和部署Web应用
    TOMCAT-报错The BASEDIR environment variable is not defined correctly
    Kafka vs RocketMQ——多Topic对性能稳定性的影响-转自阿里中间件
  • 原文地址:https://www.cnblogs.com/LionelMessi/p/7446334.html
Copyright © 2011-2022 走看看