双重检查锁实现单例模式的线程安全问题

一、结论 

双重校验锁的单例模式代码如下:

public class Singleton {
  private static Singleton singleton;

  private Singleton() {}

  public static Singleton getSingleton() {
    if (singleton == null) { // 1
      synchronized (Singleton.class) { // 2
        if (singleton == null) { // 3
          singleton = new Singleton(); // 4
        }
      }
    }
    return singleton;
  }
}

假设有两个线程AB同时访问上面这段代码,它并不能保证线程安全。

二、问题说明

  1、指令重排序的简单说明

  重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

  (1)编译器指令重排序

    编译器在不改变程序 执行结果的前提下,可以对程序的执行顺序进行优化重新排序

     (2) 处理器指令重排序

    参考:https://blog.csdn.net/javazejian/article/details/72772461 (处理器指令重排)

  2、 对象创建过程

    分为三步,如下图:

             

我们认为程序应该是按照1、2、3的步骤走下去,但实际上可能不是这样的,这里编译器和处理器可能会对2、3步的执行顺序进行重排序,即先将对象的引用指向内存空间,实际上A线程返回的是没有初始化的对象,然后B线程访问上面这段代码,判断if (singleton == null) { // 1 就为false,它会认为Singleton类已经实例化,问题就出在这里。

  重排序后A 、B线程执行时序图如下:

     

 三、解决方案

  1、不允许对象创建过程中2、3步发生指令重排序 (基于volatile的解决方案)

   即将Singleton声明时加上volatile,volatile关键字可以保证内存可见性和禁止指令重排序,关于volatile参见https://blog.csdn.net/javazejian/article/details/72772461 (volatile内存语义)

    修改后的代码:

    public class Singleton {
      private volatile static Singleton singleton;

      private Singleton() {}

      public static Singleton getSingleton() {
        if (singleton == null) { // 1
          synchronized (Singleton.class) { // 2
            if (singleton == null) { // 3
              singleton = new Singleton(); // 4
            }
          }
        }
        return singleton;
      }
    }

    Singleton属性被加上volatile后,4中对象创建过程的2、3两步在多线程环境下就被禁止重排序,这样就能保证线程安全。

  2、允许对象创建过程中2、3重排序,但不允许其他线程看到这个重排序 (基于类初始化的解决方案)

    JVM在类的初始化阶段,会执行类的初始化。在执行类的初始化期间,JVM会获取一个锁,这个锁可以同步多个线程对同一个类的初始化。基于这个特性修改代码如下:

    public class Singleton {
      private static class SingletonHolder{
        public static Singleton singleton = new Singleton();
      }
      public static Singleton getSingleton(){
        return SingletonHolder.singleton;
      }
    }

        多线程访问上面这段程序的时序图如下:

   

 

 参考资料:

  1、https://blog.csdn.net/javazejian/article/details/72772461

  2、《Java并发编程的艺术》第三章 Java内存模型

    

说明:菜鸟一枚,第一次发技术博客,如有错误或者写的不好的地方欢迎大家指正。

转载于:https://www.cnblogs.com/-Marksman/p/9219274.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

摇杆控制方向原理_为工业安全守好”门”!各种方向的控制阀原理图大集合

单向的、换向的....你想了解的方向控制阀都在这里了!方向控制阀门液压阀是用来控制液压系统中油液的流动方向或调节其流量和压力的。方向控制阀作为液压阀的一种,利用流道的更换控制着油液的流动方向。单向型方向控制阀是只允许气流沿一个方向流动的方向…

对于新生代农民工,你有什么想说的?

昨晚上这个新闻很多人转,但是可能很少有人知道他的链接出处,链接来自于http://www.mohrss.gov.cn/SYrlzyhshbzb/jiuye/gzdt/202108/t20210816_420736.html我记得我还在上小学的时候,我们家有干不完的农活,暑假每天都要下田干活&am…

谁是经营之神

—北京维富友携手北京服装学院开展服装ERP沙盘大赛时间4月21日,地点北京服装学院,参加人数35人,参加人员北京维富友软件公司高级讲师、服装学院教师和同学共35人。目的:为了让学生更好的掌握服装企业管理和盈利管控,北…

在Android初次的前期学习中的二个小例子(2)

Hello13:SQLite数据库 一、简述SQLite的概念和主要特性SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,使用非常方便,支持SQL语法标准和数据库事务原则。相对于SharedPreferences使用文件保存数据,SQLite具有…

access 增加字段 工具_Java效率工具之Lombok

作者:LiWenD正在掘金来源:https://juejin.im/post/5b00517cf265da0ba0636d4b上一篇:数据库查询速度优化之解决技巧还在编写无聊枯燥又难以维护的POJO吗?洁癖者的春天在哪里?请看Lombok!在过往的Java项目中&…

一文读懂 | 进程并发与同步

并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理多个任务的能力。并发和并行看起来很像,但实际上是有区别的,如下图(图片来源于网络):concurrency-parallelism上图的意思是&a…

快速完成和读懂测试计划

每个项目测试计划都会不一样,但是一般情况下,每个公司都会有相应的模板,尤其是项目很频繁的公司,相对应的模板应该就更全面,并且更容易修改,更能适应新项目。 并且,经常接触测试计划的人可能会察…

rust卡领地柜权限_RFID智能医疗耗材柜,上海智能高值耗材柜,国药智能医用耗材柜...

近几年因为我们的医疗改革一直在进步并改革,国家对我们的医疗方面的补助也有了明显的加大投入,与此同时让各种公立私立医院如雨后春笋般层出不穷,各大医院为了在医疗市场占有一席之地,都在各个方面开始想办法提升自己医院的水准。…

刚接触电子时,有过哪些百思不得其解的问题?

青少年时期,刚接触电子时,出于好奇,对这方面的东西也比较关注,但同时也衍生了一些百思不得其解的疑问,比如...01物理书里说大地是导体,那为什么我的小灯珠却不亮!?02初三时学了物理的…

建立管理SQL Server登录帐户

1、打开SQL Server 2005的管理工具,选择以windows身份验证模式登陆。然后右击服务器选择属性。2、在打开的服务器属性页面中,选择“安全性”做如下图设置:3.在windows上新建三个组:ReceptionEmployees,ITEmployees。4、然后在SQL …

属性

属性 属性的作用就是保护字段、对字段的赋值和取值进行限定。 属性的本质就是两个方法,一个叫get()一个叫set()。 既有get()也有set()我们诚之为可读可写属性。 只有get()没有set()我们称之为只读属性 没有get()只有set()我们称之为只写属性 private char _gender; …

c++ doxygen 注释规范_利用Doxygen给C程序生成注释文档

利用Doxygen为C程序生成注释文档一、Doxygen工具的安装利用Doxygen工具生成API帮助文档需要下载安装以下三个软件:(1)Doxygen:可以从一套归档源文件开始,生成HTML格式的在线类浏览器,或离线的LATEX、RTF参考手册。本文中所使用的版…

【2021新版】一线大厂 Go 面试题合集

秋天到了,又到了工程师们躁动不安,蠢蠢欲动的季节~这不,金九银十已然到了家门口,现在后台就有不少人问我:现在外边大厂面试都问啥想去大厂又怕面试挂面试应该怎么准备Go 开发前景如何啥样的后端适合切 Go 技术栈...面试…

python开发【基础二】

基本数据类型: 1、数字 在Python2中,分为整形(int)和长整形(long)。 在Python3中,都是int。 #1、将字符串转换为数字 a "123" v int(a) print(v) print(type(v))#2、当前数字的二进制,至少用几位表示 1位等于8个字节.…

微软白皮书发布:在IIS7.5中用Service Bus端点寄宿WCF服务

公告:本博客为微软云计算中文博客的镜像博客。部分文章因为博客兼容性问题,会影响阅读体验。如遇此情况,请访问原博客。 针对如何在IIS中寄宿Service Bus的问题,微软官方提供了白皮书详细介绍了解决方案。有兴趣的读者可以从下面的…

oracle数据库时分秒格式_Oracle如何输出指定格式的日期时间数据呢?

摘要:下文讲述Oracle数据库输出指定的日期时间格式的方法分享,如下所示;实现思路:使用TO_CHAR系统函数,指定输出格式为*******,即可将日期时间转换为指定格式的字符串如:SELECT TO_CHAR(SYSDATE,YYYY/MM/DD) AS MONTH …

can总线资料

应知识星球的同学要求,整理了一些can总线资料。在公众号后台回复 【can总线】获取资料截图推荐阅读:专辑|Linux文章汇总专辑|程序人生专辑|C语言我的知识小密圈

Go语言基础之4--流程控制

一、if else语句 1.1 基本语法1 语法1: if condition { //do something } 语法2: if condition { //do something } else { //do something } 语法3: if condition { //do something } else if condition { //do something } else { //do som…

正则表达式 学习笔记3.4

第一段为合法html代码&#xff0c;第二段为不合法html代码。<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />[^<]匹配非<的任意多个字符\\w[^<]</\\w>;开头用来匹配开始tag结尾用来匹配结束tag中间用来匹配文本。…

大家都挺难的​

这是昨天小号发的一个推文小时候觉得读书苦 但是现在想想&#xff0c;如果那时候跟现在一样用功 考上北大清华都没问题然后想到跟我家小云聊天的内容&#xff0c;挺有感触的。小云最近在我们小区的业委会上班&#xff0c;业委会在我家旁边&#xff0c;上班就是从这栋楼到另一栋…