那这种方式兼顾了性能和线程安全,而且也是懒加载的,那现在我们来创建一个类,这个类名也是懒加载的,双重检查首先从上至下是一个时间,线程0,因为刚刚呢,我们debug的时候,是从0开始的,所以现在演示的是一个单线程的情况,注意1234这几个步骤,首先分配对象的内存空间,然后正常来说要初始化这个对象,然后设置instance指向内存空间,这个instance就是我们代码里面lazyDoubleCheckSingleton,只所以没有命名成instance,是为了下载到源码之后,很好的区分,所以我们每个单例模式里面,对象的命名都会和单例模式有关,然后初始访问对象,这个初次访问对象,也就是线程0开始访问这个对象了,所以2和3怎么换顺序,4都是在最后,2和3的重排序,对结果并没有影响,但是重排序并不是百分百命中的,是有一定概率的,但是这种隐患我们一定要消除,这个是单线程没有什么问题,看一下多线程模拟一下

首先时间还是从上至下,线程0从左侧划分,右侧是线程1,首先第一个时间点分配对象的内存空间,假如线程0开始重排序了,先设置了instance指向了内存空间,这个时候线程1从上至下判断instance是否为null,这个时候判断出来了,instance并不为Null,因为他有指向内存空间,然后线程1开始访问对象,也就是线程1比线程0更早的访问对象,所以线程1访问到的对象呢,是一个在线程0中还没有被初始化成的对象,那这个时候就有问题了,这个对象并没有被完整的初始化上,系统就要报异常了,那对于2和3的重排序刚刚也说了,并不影响线程0的第4个步骤,访问了这个对象,我们知道了问题所在,我们怎么解决呢,我们可以不允许2和3重排序,或者允许线程0的2和3重排序,但是不允许线程1看到这个重排序,那我们首先可以让2和3不允许重排序
package com.learn.design.pattern.creational.singleton;/*** DoubleCheck关注的是什么呢* 双重检查* 在哪里检查* * * @author Leon.Sun**/
public class LazyDoubleCheckSingleton {/*** 我们声明volatile* 我们只要做这么一个小小的修改* 就可以实现线程安全的延迟初始化* 这样重排序就可以被禁止* 那在多线程的时候呢* CPU也有共享内存* 我们在加了volatile关键字之后* 所有线程都能够看到共享内存的执行状态* 保证了内存的可见性* 那这里面就和多线程有关了* 关于volatile修饰的共享变量呢* 在进行写操作的时候* 会多出一些汇编代码* 起到两个作用* 第一是将当前处理器缓存好的数据缓存到数据内存* 那这个写回到内存的操作呢* 回写到其他内存缓存了* 该内存的地址数据无效* 那因为其他CPU内存数据无效了* 所以他们又从共享内存共享数据* 这样呢就保证了内存的可见性* 这里面主要是用了缓存一致性协议* 那当处理器发现我这个缓存已经无效了* 所以我在进行操作的时候* 会重新从系统内存中把数据读到处理器的缓存里* 那我们就不深入讲解这块了* 再讲就要到汇编和信号的问题了* 我们的重点还是单例模式* 通过volatile和doublecheck这种方式呢* 既兼顾了性能又兼顾了线程* */private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;private LazyDoubleCheckSingleton(){}/*** 首先我们的方法不用锁了* 也就是说public static LazyDoubleCheckSingleton getInstance()这个方法一调到这里* 就立刻锁上* 而是把锁定放在方法体中* 对象还是进行一个判断* 判断完成之后* 这个时候呢* * Thread0在if(lazyDoubleCheckSingleton == null)这里* 这个instance是null* 我们切到Thread1上* Thread1进入if* 第二重判断* 我们重点关注DoubleCheck* 我们再切换到Thread0* 因为lazyDoubleCheckSingleton为空* 所以Thread0也可以进来* 但是在synchronized (LazyDoubleCheckSingleton.class)会被block掉* 那我们再切换到Thread1上* Thread1单步走* 开始new* 这个时候已经new完了* Thread1现在释放了这个锁* 所以切回到Thread0* Thread0获取这个锁之后* 进入第二个if判断* 这个时候进入第二层的空判断* 那他判断lazyDoubleCheckSingleton这个对象并不为空* 所以他直接走到这里* 然后在单步走* 走到return* 注意他后面是425* 而Thread1也是425* 因为是debug* 执行非常快* 因为我们通过volitale关键字已经去new对象的时候* 有可能出现的重排序已经解决了* 那我们直接F6单步走* 现在我们看一下console* 并且在创建对象的时候* 希望能理解doublecheck* 单例模式的一个演进* 一定要学会多线程debug的实战技能* 非常重要* 那刚刚我们也说了* 对这种重排序* 我们有两种解决方案* 第一是不允许2和3重排序* 还有一种方案允许23重排序* 但是不允许其他线程看到这个重排序* 刚刚我们是基于不允许23重排序来解决的* 接下来我们使用第二种方式来解决这个问题* 同时讲解一下原理* * * * * * @return*/public static LazyDoubleCheckSingleton getInstance(){if(lazyDoubleCheckSingleton == null){/*** 判断完成之后我们锁定这个单例的这个类* synchronized (LazyDoubleCheckSingleton.class)* 注意这里* 我们现在锁定了这个类* 那也就代表着if是进来的* 至少进到这个里面的线程到if(lazyDoubleCheckSingleton == null)这里的时候* 这个对象还是空的* 那我们想象一下* 因为if(lazyDoubleCheckSingleton == null)这里没有锁* 所以如果另外一个线程进来* 如果判断if(lazyDoubleCheckSingleton == null)它为空* 那到synchronized (LazyDoubleCheckSingleton.class)* 也会阻塞* 如果进入到这里面的* 已经把这个对象生成好了* 那刚刚新进来的线程呢* if(lazyDoubleCheckSingleton == null)判断的时候* 会直接return* 这里面还有一个小坑* 就是指定重排序的问题* 那一会讲到这里再说* 加锁之后我们肯定还要做一层空的判断* 这个lazyDoubleCheckSingleton对象如果为null的话* 这个时候我才会给他赋值* lazyDoubleCheckSingleton这个对象我们平时使用的时候* 一般命名成instance* 只不过我们这里要讲多种方式* 通过命名也加以区分* 免得弄混了* 这个instance就是单例的对象* 那我们看一下现在的这种写法* synchronized (LazyDoubleCheckSingleton.class)不加锁* 不为null就直接返回* 如果为null的话也只有一个线程进入到这里面* 这个就可以大量的减少synchronized加载方法上的时候带来的性能开销* 看上去我们的这个实现非常完美* 多个线程的时候我们通过加锁来保证只有 一个线程来创建对象* 当对象创建好之后呢* 以后再调用getInstance方法的时候* 都不会再需要加锁* 直接返回已创建好的对象* 这里面有个隐患* 那隐患出在上面的if判断* 还有lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();* 现在说一下为什么* 首先在上面的if判断的时候* 虽然判断了这个对象是不是为空* 这个时候是有可能不为空的* 虽然他不为空* 但是这个对象可能还没有完成初始化* 也就是lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();还没有执行完成* 那我们来看一下这个new的这块代码* 当我把这个对象new出来的时候* 看上去是一行* 实际上这里面经历了三个步骤* 我们加一个注释写到这里吧* 第一步分配内存给这个对象* 也就是给这个对象分配内存* 第二步初始化这个对象* 第三步设置lazyDoubleCheckSingleton指向刚分配的内存地址* 也就是这一行执行了三个操作* 那在2和3的时候* 可能会被重排序* 也就是说呢* 2和3的顺序有可能会被颠倒* 变成这样的* 先分配这个内存给这个对象* 然后单例对象指向刚分配的内存地址* 注意现在已经指向了这个内存地址* 所以这里空判断的时候呢* 并不为空* 但是这个单例对象有可能没有初始化完成* 这里面就要说一下* JAVA语言规范里面有说* 所有线程在执行JAVA程序时* 必须遵守intra-thread semantics这么一个约定* 他保证重排序不会改变单线程内的程序执行结果* 例如123这几个执行步骤* 对于单线程来说* 2和3互换位置* 其实不会改变单线程的执行结果* 所以JAVA语言规范允许那些在单线程内不会改变单线程执行结果的重排序* 也就是说2和3是允许的* 因为这个重排序可以提高程序的执行性能* 为了更好地理解呢* 画了一个图给大家看一下* * * * */synchronized (LazyDoubleCheckSingleton.class){if(lazyDoubleCheckSingleton == null){lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();//1.分配内存给这个对象
//                  //3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址//2.初始化对象
//                    intra-thread semantics
//                    ---------------//3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址}}}return lazyDoubleCheckSingleton;}
}
package com.learn.design.pattern.creational.singleton;public class T implements Runnable {@Overridepublic void run() {
//        LazySingleton lazySingleton = LazySingleton.getInstance();
//        System.out.println(Thread.currentThread().getName()+"  "+lazySingleton);/*** 调用他的getInstance方法* */LazyDoubleCheckSingleton instance = LazyDoubleCheckSingleton.getInstance();
//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();;//        ContainerSingleton.putInstance("object",new Object());
//        Object instance = ContainerSingleton.getInstance("object");
//        ThreadLocalInstance instance = ThreadLocalInstance.getInstance();System.out.println(Thread.currentThread().getName()+"  "+instance);}
}
package com.learn.design.pattern.creational.singleton;public class T implements Runnable {@Overridepublic void run() {
//        LazySingleton lazySingleton = LazySingleton.getInstance();
//        System.out.println(Thread.currentThread().getName()+"  "+lazySingleton);/*** 调用他的getInstance方法* */LazyDoubleCheckSingleton instance = LazyDoubleCheckSingleton.getInstance();
//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();;//        ContainerSingleton.putInstance("object",new Object());
//        Object instance = ContainerSingleton.getInstance("object");
//        ThreadLocalInstance instance = ThreadLocalInstance.getInstance();System.out.println(Thread.currentThread().getName()+"  "+instance);}
}

DoubleCheck双重检查实战及原理解析相关推荐

  1. RocketMQ实战与原理解析

    网站 更多书籍点击进入>> CiCi岛 下载 电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击"普通下载" ...

  2. 从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析

    API 网关 API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题 ...

  3. rocketmq java实战_RocketMQ实战与原理解析 杨开元著 PDF下载

    推荐序 前言 第1章 快速入门1 1.1 消息队列功能介绍1 1.1.1 应用解耦1 1.1.2 流量消峰2 1.1.3 消息分发3 1.2 RocketMQ简介4 1.3 快速上手RocketMQ4 ...

  4. 《KVM虚拟化技术实战和原理解析》读书笔记(三)

    第四章 KVM核心基础功能 在硬件虚拟化技术的支持下,内核的KVM模块和QEMU的设备模拟协同工作,就构成了一整套与物理计算机系统完全一致的虚拟化的计算机软硬件系统. 4.1 硬件平台和软件版本说明 ...

  5. clickhouse原理解析与开发实战 pdf_重识SSM,“超高频面试点+源码解析+实战PDF”,一次性干掉全拿走...

    重识SSM,"超高频面试点"+"源码解析"+"实战PDF",一次性干掉全拿走!! 01 超高频面试点知识篇 1.1 Spring超高频面试点 ...

  6. 双重检查(Double-Check)

    有一个优化的思路,就是把100%出现的防护盾,也改为1%的几率出现,使之只出现在可能会导致多个实例出现的地方. 代码如下: // 代码三 public class Singleton {private ...

  7. Java 中的双重检查(Double-Check)

    转载自    Java 中的双重检查(Double-Check) 在 Effecitve Java 一书的第 48 条中提到了双重检查模式,并指出这种模式在 Java 中通常并不适用.该模式的结构如下 ...

  8. flask web开发是前端还是后端_Flask Web开发实战:入门、进阶与原理解析 PDF 全格式版...

    给大家带来的一篇关于Flask相关的电子书资源,介绍了关于Flask.Web.开发实战方面的内容,本书是由机械工业出版社出版,格式为PDF,资源大小12.2M,李辉编写,目前豆瓣.亚马逊.当当.京东等 ...

  9. 大规模分布式存储系统:原理解析与架构实战 (大数据技术丛书) - 电子书下载 -(百度网盘 高清版PDF格式)...

    大规模分布式存储系统:原理解析与架构实战 (大数据技术丛书)-杨传辉 在线阅读                   百度网盘下载(89hy) 书名:大规模分布式存储系统:原理解析与架构实战 (大数据技 ...

最新文章

  1. python使用matplotlib可视化棉签图、棉棒图(stem plot)、展示离散而有序的数据
  2. js 包含某个字符串_[译] 5 大 JavaScript 字符串操作库
  3. JavaScript表单验证,输入中文时字符长度为2
  4. [Linux] 不带JRE版本的LumaQQ安装方法
  5. Java Review - Java进程内部的消息中间件_Event Bus设计模式
  6. 区间贪心算法-——活动安排问题
  7. 5位数的数字黑洞是多少_每日一题[491]数字黑洞--Kaprekar常数
  8. 数据科学包1---numpy
  9. JS 中的== 与 ===
  10. Python | 类和对象
  11. sqlite创建表格
  12. HTML中的空格、Tab、书名号大于号以及常用特殊符号
  13. C1认证:植物大战僵尸存档详解
  14. 过了面试入职被排挤,离职是最坏的做法!
  15. vue给url 中文参数 添加编码解码
  16. 研究员发现针对 Windows 用户的 Raspberry Robin 蠕虫
  17. 图像预处理: 规范化
  18. NOIP simulation
  19. 新入手的Mac安装开发软件
  20. 免安装,在线解析下载720全景图

热门文章

  1. oracle 数据库的数据事务的相关概念
  2. Eclipse 中隐藏的 5 个非常有用的功能
  3. Java多线程初学者指南(5):join方法的使用
  4. POJ1063 Flip and Shift
  5. XHTML学习笔记 Part2:核心元素
  6. 在Eclipse中给JRE-Library添加本地Javadoc
  7. C# 操作其他进程ListView
  8. 制作类似QQ截图软件
  9. 理解并使用ASP.NET的高级配置
  10. C++文件流操作备忘录