• 饿汉式单例模式与懒汉式单例模式


           了解单例设计模式的人都知道,单例中涉及的类他在内存之中始终是独一份存在的,如果存在两份则将出现问题,并且单例模式有两种相对比较有特点的形式,那就是饿汉式与懒 汉式单例模式,在本节中我们将会详细讲解单例设计模式的两种形式,并且我们将会讲解如 何在多线程的情况下使用单例设计模式;

          1. 饿汉式单例模式

    所谓饿汉式单例设计模式,就是将类的静态实例作为该类的一个成员变量,也就是说在 JVM 加载它的时候就已经创建了该类的实例,因此它不会存在多线程的安全问题,详细代码 请看如下:

    public class SingleTest {
    
    private final static SingleTest instance = new SingleTest();
    
    private SingleTest(){
    
    }
    public static SingleTest newInstance(){
    return instance;
    }
    }

            可以看到上述代码中的单例不存在线程安全的问题,但是他有一个性能上面的问题,那 就是提前对实例进行了初始化或者说构造,假设构造该类需要很多的性能消耗,如果代码写 成这个样子将会提前完成构造,又假设我们在系统运行过程中压根就没有对该实例进行使用, 那岂不是很浪费系统的资源呢?因此单例设计模式其实还有下一个小节的版本;

      2 懒汉式单例模式

    所谓懒汉式单例模式的意思就是,实例虽然作为该类的一个实例变量,但是他不主动进 行创建,如果你不使用它那么他将会永远不被创建,只有你在第一次使用它的时候才会被创 建,并且得到保持;请看下一段代码

    public class SingleTest2 {
    
        private static SingleTest2 instance = null;
    
        private SingleTest2() {
        }
    
        public static SingleTest2 newInstance() {
            if (null == instance) {
                instance = new SingleTest2();
            }
            return instance;
        }
    
    }

    上述的代码就是我们所说的懒汉式单例模式,但是根据上文中的关于线程安全问题的分 析我们不难发出现,instance 有可能会被创建两次,至于原因为什么呢?让我们根据之前的 IO Programming 系列丛书 由于个人能力有限,书中难免会有偏颇之处,希望读到这本书的朋友能够给予我指导和批评,欢迎你们 的指正 知识点来慢慢分析,然后总结出来一个最优的懒汉式单例模式

     根据我们之前的知识点,运行中的程序很有可能会出现上图所出现的情况。也就是 A 线程和 B 线程有可能同时执行到了 null==instance 的部分他们判断到了 instance 没有被创建, 因此分别实例化了一个以上的 instance,这样的单例类将是非常危险的,那么我们应该如何 避免多线程引起的问题呢,看到这里您可能想到了用 synchronized 这个关键字来解决问题, 于是有了如下的代码

    public synchronized static SingleTest newInstance()
    {
    if(null==instance)
    {
    instance = new SingleTest();
    }
    return instance;
    }

    但是该方法的效率将是相当低下的,因为每一次调用都要获取锁,判断锁的状态,因此 就会出现解决了安全问题,带来了效率问题,当然安全问题和效率问题本来就是两个很不可 调和的矛盾,但是我们也不应该就此妥协,需要尽我们的智慧既解决了安全问题又带来了最 小的效率影响;我们将程序写成如下的样子

    public static SingleTest2 newInstance() {
            if (null == instance) {
                synchronized (SingleTest2.class) {
                    if (null == instance) {
                        instance = new SingleTest2();
                    }
                }
            }
            return instance;
        }

    好的,让我们继续以图示的方式来说明一下,上述代码带来了哪些改变又如何将效率的 损耗降到了最低

    通过上述代码的分析,我们不难发现,锁的等待或者争抢最多发生两次,也就是同步代 码块中的代码最多被执行两次,如此一来,安全问题解决了,效率问题也被解决掉了。

    3,指令重排优

    volatile关键字另一个作用就是禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象,关于指令重排优化参考文章 https://www.cnblogs.com/yuluoxingkong/p/9236077.html

    public class SingleTest2 {
        //以关键字volatile修饰之后,就会阻止JVM对其相关代码进行指令重排,这样就能够按照既定的顺序指执行。
        //参考文档 <p>https://www.cnblogs.com/yuluoxingkong/p/9288477.html</p>
        private volatile static SingleTest2 instance = null;
    
        private SingleTest2() {
        }
    
        public static SingleTest2 newInstance() {
            if (null == instance) {
                synchronized (SingleTest2.class) {
                    if (null == instance) {
                        instance = new SingleTest2(); //非原子操作,可能会出现指令重排优
                    }
                }
            }
            return instance;
        }
    
    }
  • 相关阅读:
    网页加速的14条优化法则 网站开发与优化
    .NET在后置代码中输入JS提示语句(背景不会变白)
    C语言变量声明内存分配
    SQL Server Hosting Toolkit
    An established connection was aborted by the software in your host machine
    C语言程序设计 2009春季考试时间和地点
    C语言程序设计 函数递归调用示例
    让.Net 程序脱离.net framework框架运行
    C语言程序设计 答疑安排(2009春季 110周) 有变动
    软件测试技术,软件项目管理 实验时间安排 2009春季
  • 原文地址:https://www.cnblogs.com/yuluoxingkong/p/9802418.html
Copyright © 2020-2023  润新知