如何用JAVA语言分析双重检查锁定

1、双重检查锁定

在程序开发中,有时需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化,此时可以采用双重检查锁定来延迟对象初始化操作。双重检查锁定是设计用来减少并发系统中竞争和同步开销的一种软件设计模式,在普通单例模式的基础上,先判断对象是否已经被初始化,再决定要不要加锁。尽管双重检查锁定解决了普通单例模式的在多线程环境中易出错和线程不安全的问题,但仍然存在一些隐患。下面以JAVA语言源代码为例,分析双重检查锁定缺陷产生的原因以及修复方法。

2、 双重检查锁定的危害

双重检查锁定在单线程环境中并无影响,在多线程环境下,由于线程随时会相互切换执行,在指令重排的情况下,对象未实例化完全,导致程序调用出错。

3、示例代码

示例源于Samate Juliet Test Suite for Java v1.3 (https://samate.nist.gov/SARD/testsuite.php),源文件名:CWE609_Double_Checked_Locking__Servlet_01.java

3.1缺陷代码

如何用JAVA语言分析双重检查锁定

上述代码行23行-38行,程序先判断 stringBad 是否为 null,如果不是则直接返回该 String 对象,这样避免了进入 synchronized 块所需要花费的资源。当 stringBad 为 null 时,使用 synchronized 关键字在多线程环境中避免多次创建 String 对象。在代码实际运行时,以上代码仍然可能发生错误。

对于第33行,创建 stringBad 对象和赋值操作是分两步执行的。但 JVM 不保证这两个操作的先后顺序。当指令重排序后,JVM 会先赋值指向了内存地址,然后再初始化 stringBad 对象。如果此时存在两个线程,两个线程同时进入了第27行。线程1首先进入了 synchronized 块,由于 stringBad 为 null,所以它执行了第33行。当 JVM 对指令进行了重排序,JVM 先分配了实例的空白内存,并赋值给 stringBad,但这时 stringBad 对象还未实例化,然后线程1离开了 synchronized 块。当线程2进入 synchronized 块时,由于 stringBad 此时不是 null ,直接返回了未被实例化的对象(仅有内存地址值,对象实际未初始化)。后续线程2调用程序对 stringBad 对象进行操作时,此时的对象未被初始化,于是错误发生。

使用360代码卫士对上述示例代码进行检测,可以检出“双重检查锁定”缺陷,显示等级为中。在代码行第27行报出缺陷,如图1所示:

如何用JAVA语言分析双重检查锁定

图1:“双重检查锁定”的检测示例

3.2 修复代码

如何用JAVA语言分析双重检查锁定

在上述修复代码中,在第23行使用 volatile 关键字来对单例变量 stringBad 进行修饰。 volatile 作为指令关键字确保指令不会因编译器的优化而省略,且要求每次直接读值。

由于编译器优化,代码在实际执行的时候可能与我们编写的顺序不同。编译器只保证程序执行结果与源代码相同,却不保证实际指令的顺序与源代码相同,在单线程环境中并不会出错,然而一旦引入多线程环境,这种乱序就可能导致严重问题。 volatile 关键字就可以从语义上解决这个问题,值得关注的是 volatile 的禁止指令重排序优化功能在 Java 1.5 后才得以实现,因此1.5 前的版本仍然是不安全的,即使使用了 volatile 关键字。

使用360代码卫士对修复后的代码进行检测,可以看到已不存在“双重检查锁定”缺陷。如图2:

如何用JAVA语言分析双重检查锁定

图2:修复后检测结果

4 、如何避免双重检查锁定

要避免双重检查锁定,需要注意以下几点:

(1)使用 volatile 关键字避免指令重排序,但这个解决方案需要 JDK5 或更高版本,因为从JDK5 开始使用新的 JSR-133 内存模型规范,这个规范增强了 volatile 的语义。

(2)基于类初始化的解决方案。

如何用JAVA语言分析双重检查锁定JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。

关于如何用JAVA语言分析双重检查锁定就分享到这里啦,希望上述内容能够让大家有所提升。如果想要学习更多知识,请大家多多留意小编的更新。谢谢大家关注一下亿速云网站!

文章标题:如何用JAVA语言分析双重检查锁定,发布者:亿速云,转载请注明出处:https://worktile.com/kb/p/27246

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
亿速云的头像亿速云认证作者
上一篇 2022年9月20日 上午12:11
下一篇 2022年9月20日 上午12:12

相关推荐

  • windows罗技驱动常见问题怎么解决

    罗技驱动怎么用 1、首先我们百度搜索罗技驱动,进入罗技的网站。 2、进入后在上方选中“支持”,进入“软件下载” 3、然后点击下载下方的“下载与应用程序” 4、接着在软件下方找到并进入“罗技G HUB” 5、在上方可以选择我们的系统版本。 6、选择完成后点击下方的“download now” 7、然后…

    2022年9月20日
    1.2K00
  • windows KB4525237安装失败怎么解决

    根据情况不同,小编为大家准备了四种解决方案 名列前茅种方案: 重新下载安装补丁 在我们下载补丁的过程中可能就会遇到文件损坏的情况,所以破损的补丁安装包是无法安装成功的。 这种情况下可以尝试重新下载一个补丁安装包重新安装。 第二种方案: 更新升级最新的.net framework(目前是4.8版本) …

    2022年9月1日
    41900
  • 电脑中的五角星怎么打出

    电脑中的五角星打出方法: 1、由于键盘上没有五角星,所以我们需要借助输入法。 2、不同输入法界面略有不同,下面以搜狗输入法为例。 3、首先打开语言栏,点击最右边的图标,打开“智能输入助手” 4、打开后,找到并点开“符号大全” 5、然后进入左上角的“特殊符号” 6、最后在其中点击“五角星”符号就可以打…

    2022年8月31日
    94100
  • Vue前端怎么整合ElementUi

    Vue前端整合Element Ui Element UI 是一套采用 Vue 2.0 作为基础框架实现的组件库,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库,提供了配套设计资源,帮助网站快速成型。 1.安装Element Ui组件库 WIN+R输入CMD(必须以管理员身份运行)…

    2022年9月21日
    42800
  • Redis实现限流器的方法有哪些

    方法一:基于Redis的setnx的操作 我们在使用Redis的分布式锁的时候,大家都知道是依靠了setnx的指令,在CAS(Compare and swap)的操作的时候,同时给指定的key设置了过期实践(expire),我们在限流的主要目的就是为了在单位时间内,有且仅有N数量的请求能够访问我的代…

    2022年9月10日
    58900
  • 怎么用好MySQL索引

    为了更好地进行解释,我创建了一个存储引擎为InnoDB的表user_innodb,并批量初始化了500W+条数据。包含主键id、姓名字段(name)、性别字段(gender,用0,1表示不同性别)、手机号字段(phone),并为name和phone字段创建了联合索引。 CREATE TABLE `u…

    2022年9月21日
    68000
  • 怎么利用蓝牙功能两秒内攻击解锁Tapplock智能挂锁

    几个星期前,Youtube上名为JerryRigEverything的人上传了一段针对 Tapplock 智能蓝牙指纹挂锁的拆解视频。视频显示,只要用一个GoPro相机固定粘附底座就可以把 Tapplock 后盖取下,然后再利用螺丝刀就能把 Tapplock 的锁扣轻松打开。 有点不可思议,这段视频…

    2022年9月10日
    59000
  • how2heap注意点有哪些

    first-fit 我的理解是分割unsortedbin里面名列前茅个大于要分配的chunk,但是实际上并不是这样 测试程序 #include <stdio.h>#include <stdlib.h>#include <string.h>int main(){ i…

    2022年9月15日
    50800
  • SQL注入语法有哪些

    仅供参考学习使用。 SQL注入语法 报错注入 updatexml(1,concat(0x7e,(select database()),0x7e),1)–+extractvalue(1,concat(0x7e,(select database()),0x7e),1)–+select count(*…

    2022年9月6日
    69200
  • mysql主从复制怎么理解

    在mysql中,主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点,默认采用异步复制方式。采用主从复制的好处:1、让主库负责写,从库负责读,当主库出现了锁表的情景,通过读从库也可以保证业务的正常运作;2、可以做数据的热备;3、进行架构的扩展,可降低磁盘I/O访问的频率,提高…

    2022年9月20日
    87700
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部