zoukankan      html  css  js  c++  java
  • 我眼中的单例模式

    说到单例模式,网上搜索出来的结果是多如牛毛,但这不影响我也来凑热闹的心情。

    任何事情都是要亲身去体会了,才能加深自己的理解。本着不断学习进取的精神,我很想可以站在牛人的肩膀上,哪怕是仰视牛人的情况下,我也想发挥自己的余热。记录下自己学习的足迹,权当自己未来细细回味也好。(不过说真的,自己试着去组织语言来介绍你的问题也好,你的产品也好,能在很大的程度上提高你的表达能力。大脑是越锻炼越活的东西,讲话、写作也一样,持之以恒,必有收获。总之,贵在坚持哦!)

    好了,我先声明下我参考的牛人文章出处:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html

    下面来介绍模式,单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点

     其实就是实现只有一个门可以进入,且每次只给一个人进入。这就像以前的一位博友所举的例子,很多人排队去厕所蹲坑一样,每一次只能让一个人去蹲坑。实现单例模式的原因,要么是资源共享,要么是控制资源等。所谓资源共享,就是因为单例模式保证了一个类仅有一个实例,所以大家访问的实例是一致的。而控制资源的话,主要是减少资源的申请与释放等。

    牛人就是牛人,一下给出了五种实现单例模式的例子。看得我茅塞顿开,大呼过瘾。

    第一种:简单实现(惰性实例化)
    namespace Singleton
    {
        public class Program
        {
            static void Main(string[] args)
            {
                Singleton s1 = Singleton.Instance;
                Singleton s2 = Singleton.Instance;
    
                if (s1 == s2)
                {
                    Console.WriteLine("Objects are the same instance");
                }
    
                Console.Read();
            }
        }
    
        public sealed class Singleton
        {
            private Singleton() { }
    
            private static Singleton instance = null;
    
            public static Singleton Instance
            {
                get 
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                    return instance;
                }
            }
        }
    }

    简单实现对于线程来说是不安全的,因为在多线程的情况下,有可能产生多个Singleton实例。多线程的情况下,如果多个线程都去判断(instance == null),而它们都还没有创建实例的情况下,就会产生多个Singleton实例。对于简单实现来讲,Singleton实例化并不是应用程序启动就创建,所以我们把它叫做“惰性实例化”,这能避免应用程序启动时实例化不必要的实例。

    第二种:安全的线程

     

    namespace Singleton
    {
        public class Program
        {
            static void Main(string[] args)
            {
                Singleton s1 = Singleton.Instance;
                Singleton s2 = Singleton.Instance;
    
                if (s1 == s2)
                {
                    Console.WriteLine("Objects are the same instance");
                }
    
                Console.Read();
            }
        }
    
        public sealed class Singleton
        {
            private Singleton() { }
    
            private static Singleton instance = null;
            private static readonly object padLock = new object();
    
            public static Singleton Instance
            {
                get 
                {
                    lock (padLock)
                    {
                        if (instance == null)
                        {
                            instance = new Singleton();
                        }
                        return instance;
                    }
                }
            }
        }
    }

    安全的线程,这是对简单实例的补充。因为提供了加锁lock()的操作,这就能确保只有一个线程进入。但是加锁需要增加额外的开销,损失性能。

    第三种:双重锁定检查
    namespace Singleton
    {
        public class Program
        {
            static void Main(string[] args)
            {
                Singleton s1 = Singleton.Instance;
                Singleton s2 = Singleton.Instance;
    
                if (s1 == s2)
                {
                    Console.WriteLine("Objects are the same instance");
                }
    
                Console.Read();
            }
        }
    
        public sealed class Singleton
        {
            public Singleton() { }
    
            private static Singleton instance = null;
    private static readonly object padLock = new object(); public static Singleton Instance { get { if (instance == null) { lock (padLock) { if (instance == null) { instance = new Singleton(); } } } return instance; } } } }

    双重锁定检查安全的线程上面又进行了改进,主要是考虑了每次加锁会增加额外的开销,影响性能。所以在加锁前再判断Singleton有没有被实例化。这样,它就能减少很多的额外开销且是线程安全的。实际上,应用程序很少需要上面方式的实现。这种方式仍然有很多缺点:无法实现延迟初始化。大多数情况下我们会使用静态初始化的方式。

    第四种:静态初始化
    namespace Singleton
    {
        public class Program
        {
            static void Main(string[] args)
            {
                Singleton s1 = Singleton.Instance;
                Singleton s2 = Singleton.Instance;
    
                if (s1 == s2)
                {
                    Console.WriteLine("Objects are the same instance");
                }
    
                Console.Read();
            }
        }
    
        public sealed class Singleton
        {
            static readonly Singleton instance = new Singleton();
    
            private Singleton() { }
    
            public static Singleton Instance
            {
                get
                {
                    return instance;
                }
            }
        }
    }

    静态初始化是在 .NET 中实现 Singleton 的首选方法。 这段代码有点意思,我也解读一下。主要是讲解下关键字吧。

    sealed:修改类,意为这个类不可再被继承,防止子类被实例化而不能保证只有一个实例的问题。

    private Singleton():用private 修改构造函数,可以防止这个类在外部被实例。也就是在Singleton类外面想new Singleton()是会报编译错误。

    static readonly:表示只能在声明时赋值,或是在静态构造中赋值。

    第五种:延迟初始化
    namespace Singleton
    {
        class Program
        {
            static void Main(string[] args)
            {
                Singleton s1 = Singleton.Instance;
                Singleton s2 = Singleton.Instance;
    
                if (s1 == s2)
                {
                    Console.WriteLine("Objects are the same instance");
                }
    
                Console.Read();
            }
        }
    
        public sealed class Singleton
        {
            public Singleton() { }
    
            public static Singleton Instance
            {
                get
                {
                    return Delay.DelayInstance;
                }
            }
        }
    
        public sealed class Delay
        {
            private static readonly Singleton delayInstance = new Singleton();
            private Delay() { }
    
            public static Singleton DelayInstance
            {
                get
                {
                    return delayInstance;
                }
            }
        }
    }

    把实例化的工作交给Delay类开实现,这样Singleton类就实现了延迟初始化。这种方式具有很多的优势,是值得推荐的一种实现方式。但是这种方式就需要开发人员记住不能使用new关键字实例化Singleton。

    应用场景

    其实不管是对于哪个设计模式来说,我们总会想知道什么时候能用到它。毕竟东西不是白学的,是猫是狗总也得带出来溜溜。

    毕竟我也使用得少,所以这里面为了让网友能够看得大而全点,我摘自博友的内容如下:

    1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~

    2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

    3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。

    4. 应用程序的日志应用,一般都可用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

    5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

    6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为使用单例模式来维护,就可以大大降低这种损耗。

    7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

    8. 操作系统的文件系统,也是单例模式实现的具体例子,一个操作系统只能有一个文件系统。

    9. HttpApplication 也是单例模式的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例。

    当然如果你有新的使用场景,请不吝赐教:)

    至此,本文完!

  • 相关阅读:
    Java实现 LeetCode 30 串联所有单词的子串
    Java实现 LeetCode 29 两数相除
    Java实现 LeetCode 29 两数相除
    Java实现 LeetCode 29 两数相除
    Java实现 LeetCode 28 实现strStr()
    Java实现 LeetCode 28 实现strStr()
    Java实现 LeetCode 28 实现strStr()
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 27 移除元素
    字符编码终极笔记:ASCII、Unicode、UTF-8、UTF-16、UCS、BOM、Endian
  • 原文地址:https://www.cnblogs.com/csdbfans/p/3505647.html
Copyright © 2011-2022 走看看