zoukankan      html  css  js  c++  java
  • 饿汉式单例模式与懒汉式单例模式

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

          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;
        }
    
    }
  • 相关阅读:
    Heritrix源码分析(十) Heritrix中的Http Status Code(Http状态码)(转)
    Heritrix源码分析(九) Heritrix的二次抓取以及如何让Heritrix抓取你不想抓取的URL
    Heritrix源码分析(八) Heritrix8个处理器(Processor)介绍(转)
    HTML 的 iframe 元素
    CSS 选择器及其优先级
    在触屏设备中拖动 overflow 元素
    关于博客园博问标签的自我实现
    ABAP中读取EXCEL中不同的SHEET数据
    创建表索引
    Call Transaction
  • 原文地址:https://www.cnblogs.com/yuluoxingkong/p/9802418.html
Copyright © 2011-2022 走看看