zoukankan      html  css  js  c++  java
  • 设计模式课程 设计模式精讲 8-3 单例设计模式-DoubleCheck双重检查实战及原理解析

    1    课程讲解

    1.1  为何要使用双重检查

    1.2  双重检查的缺点

    1.3  指令重排序讲解

    1.4  指令重排序比喻(自己理解)

    1.5  如何解决指令重排序问题

    2    代码演练

    2.1  代码演练1(双重检查  解决对象锁和类锁的问题)

    2.2  代码演练2(volatile 应用:解决重排序问题)

    1    课程讲解
    1.1  为何要使用双重检查

    在上节课的时候,多线程的时候,由于一个线程被锁,其他的线程无法访问该类,被堵塞。性能大大降低,双重检查主要是应用解决此类问题。

    双重检查可以使更多的线程堵塞在方法中,而不是在类之外,这样的话,当锁被释放的时候,能够更快的执行,可以大大的提高效率。

    1.2  双重检查的缺点

    java在执行过程中,可能会出现指令重排序问题,(后边语句参考本节2.1代码演练2)导致a线程(先来的)的对象已经赋值,但是还没有初始化完成,这时线程b(后到的)经过判断,也开始访问对象(因为现在对象不为空),导致线程b访问的是线程a还未初始化完成的对象。由于对象并没有被完整的初始化上,系统会报异常。

    1.3  指令重排序讲解

    初始化的时候,实际进行了三个步骤:

    a  给该对象分配内存

    b  初始化该对象

    c  设置该对象指向给该对象分配的内存

    一般情况下,按照abc的顺序执行,但是也会有一定几率bc 颠倒。

    这在单线程中执行的时候并没有问题,而且能够提高运行的效率。

    1.4  指令重排序比喻(自己理解)

    可以比喻如下:

    人们来吃饭,

    a  首先食堂拿出一份饭,

    b  确定这个人是谁,

    c  这个人拿走这份饭

    1.5  如何解决指令重排序问题

    两种方法:

    a  使重排序不再发生,每个执行的进程都按照初始化的正常步骤进行 参见  本节代码2.2

    b  不允许后来的线程 看到 先来的线程进行的重排序问题

    2    代码演练
    2.1  代码演练1(解决对象锁和类锁的问题)

    测试类:

    package com.geely.design.pattern.creational.singleton;
    
    public class Test {
    
        /*public static void main(String [] args){
            //这样写异常,因为构造方法私有
    //        LazySingleton lazySingleton = new LazySingleton();
           LazySingleton lazySingleton = LazySingleton.getInstance();
           System.out.println(lazySingleton);
        }*/
    
        public static void main(String [] args){
            Thread thread1 = new Thread(new T());
            Thread thread2 = new Thread(new T());
            thread1.start();
            thread2.start();
            System.out.println("结束了!!!");
        }
    }

    线程类:

    package com.geely.design.pattern.creational.singleton;
    
    /**
     * 注:该类为线程类,调用LazySingleton
     */
    public class T implements Runnable{
    
        /*@Override
        public void run() {
            LazySingleton lazySingleton = LazySingleton.getInstance();
            System.out.println(Thread.currentThread().getName()+"==="+lazySingleton);
    
        }*/
    
        @Override
        public void run() {
            LazyDoubleCheckSingleton lazyDoubleCheckSingleton = LazyDoubleCheckSingleton.getInstance();
            System.out.println(Thread.currentThread().getName()+"==="+lazyDoubleCheckSingleton);
        }
    }

    实体类:

    package com.geely.design.pattern.creational.singleton;
    
    public class LazyDoubleCheckSingleton {
        /*
        属性私有,其他外部类,无法调用该属性,安全
         */
        private static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
    
        /**
         * 构造方法私有,其他类无法实例化该类
         */
        private LazyDoubleCheckSingleton(){
        }
    
    
        /**
         * 换一种写法,
         *
         * @return
         */
          public static LazyDoubleCheckSingleton getInstance(){
              if(lazyDoubleCheckSingleton == null){
                  synchronized (LazyDoubleCheckSingleton.class){
                      if(lazyDoubleCheckSingleton == null){
                          lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                      }
                  }
              }
              return lazyDoubleCheckSingleton;
        }
    
    
    }

    打印结果:

    "C:Program FilesJavajdk1.7.0_79injava.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:9096,suspend=y,server=n -javaagent:C:Usersweijingli.IdeaIC2018.1systemcaptureAgentdebugger-agent.jar=file:/C:/Users/weijingli/AppData/Local/Temp/capture.props -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.7.0_79jrelibcharsets.jar;C:Program FilesJavajdk1.7.0_79jrelibdeploy.jar;C:Program FilesJavajdk1.7.0_79jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.7.0_79jrelibextdnsns.jar;C:Program FilesJavajdk1.7.0_79jrelibextjaccess.jar;C:Program FilesJavajdk1.7.0_79jrelibextlocaledata.jar;C:Program FilesJavajdk1.7.0_79jrelibextsunec.jar;C:Program FilesJavajdk1.7.0_79jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.7.0_79jrelibextsunmscapi.jar;C:Program FilesJavajdk1.7.0_79jrelibextzipfs.jar;C:Program FilesJavajdk1.7.0_79jrelibjavaws.jar;C:Program FilesJavajdk1.7.0_79jrelibjce.jar;C:Program FilesJavajdk1.7.0_79jrelibjfr.jar;C:Program FilesJavajdk1.7.0_79jrelibjfxrt.jar;C:Program FilesJavajdk1.7.0_79jrelibjsse.jar;C:Program FilesJavajdk1.7.0_79jrelibmanagement-agent.jar;C:Program FilesJavajdk1.7.0_79jrelibplugin.jar;C:Program FilesJavajdk1.7.0_79jrelib
    esources.jar;C:Program FilesJavajdk1.7.0_79jrelib
    t.jar;F:xiangmu3XinIdeadesign_pattern	argetclasses;D:javadevolopKitideaanZhIntelliJ IDEA Community Edition 2018.1.4libidea_rt.jar" com.geely.design.pattern.creational.singleton.Test
    Connected to the target VM, address: '127.0.0.1:9096', transport: 'socket'
    结束了!!!
    Thread-0===com.geely.design.pattern.creational.singleton.LazyDoubleCheckSingleton@8fea539
    Disconnected from the target VM, address: '127.0.0.1:9096', transport: 'socket'
    Thread-1===com.geely.design.pattern.creational.singleton.LazyDoubleCheckSingleton@8fea539
    
    Process finished with exit code 0
    2.2  代码演练2(volatile 应用:解决重排序问题)

    java语言规范中规定:所有线程执行java程序时,必须要遵守intra-thread semantics

    intra-thread semantics 保证重排序不会改变单线程内的程序执行结果。

    换句话说,intra-thread semantics 允许那些在单线程内,不会改变单线程程序执行结果的重排序。

    package com.geely.design.pattern.creational.singleton;
    
    public class LazyDoubleCheckSingleton {
        /*
        1  volatile关键字的作用
           将当前处理器缓存行的数据写回到内存,该操作会使在其他cpu内存中缓存了该内存地址的数据无效。它们又从共享内存同步数据。  如此操作保存内存的可见性。j
        2
         */
        private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
    
        /**
         * 构造方法私有,其他类无法实例化该类
         */
        private LazyDoubleCheckSingleton(){
        }
    
    
        /**
         * 换一种写法,
         *
         * @return
         */
          public static LazyDoubleCheckSingleton getInstance(){
              if(lazyDoubleCheckSingleton == null){
                  synchronized (LazyDoubleCheckSingleton.class){
                      if(lazyDoubleCheckSingleton == null){
                          lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                      }
                  }
              }
              return lazyDoubleCheckSingleton;
        }
    
    
    }
  • 相关阅读:
    python 代码编写环境及编辑器配置
    升级gradle:Could not find method jackOptions() for arguments
    Unable to determine application id: com.android.tools.idea.run.ApkProvisionException: No outputs for the main artifact of variant: debug
    ecplice 如何智能提示(旧)
    seo 回忆录百度基本概念(一)
    正则表达针对html(九)
    读Pyqt4教程,带你入门Pyqt4 _002
    [Objective-c] 002_对象 类 变量 方法
    SD.Team字符表情集大全(持续更新中..)
    SD.Team主题形象小人偶
  • 原文地址:https://www.cnblogs.com/1446358788-qq/p/11368146.html
Copyright © 2011-2022 走看看