介绍

双重校验锁是单例模式中,饿汉式的一种实现方式。因为有两次判空校验,所以叫双重校验锁,一次是在同步代码块外,一次是在同步代码块内。

为什么在同步代码块内还要再检验一次?

第一个if减少性能开销,第二个if避免生成多个对象实例。

现有三个线程A,B,C,假设线程A和线程B同时调用getSingleton()时,判断第一层if判断都为空,这时线程A先拿到锁,线程B在代码块外层等待。线程A进行第二层if判断,条件成立后new了一个新对象,创建完成,释放锁,线程B拿到锁,进行第二层if判断,singleton不为空,直接返回singleton释放锁,避免生成多个对象实例。线程线C调用getSingleton时第一层判断不成立,直接拿到singleton对象返回,避免进入锁,减少性能开销。

为什么要用volatile关键字?

singleton = new Singleton();这行代码并不是一个原子指令,可能会在JVM中进行指令重排;

new 实例背后的指令,我们通过使用 javap -c指令,查看字节码如下:

   // 创建 Singleton 对象实例,分配内存
0: new #5
// 复制栈顶地址,并再将其压入栈顶
3: dup
// 调用构造器方法,初始化 Singleton对象
4: invokespecial #6 // Method "<init>":()V
// 存入局部方法变量表
7: astore_1

从字节码可以看到创建一个对象实例,可以分为三步:

(1)分配对象内存(给singleton分配内存)。

(2)调用构造器方法,执行初始化(调用 Singleton 的构造函数来初始化成员变量)。

(3)将对象引用赋值给变量(执行完这步 singleton 就为非 null 了)。

在 JVM 的即时编译器中存在指令重排序的优化。指令重排并不影响单线程内的执行结果,但是在多线程内可能会影响结果。也就是说上面的2和3的顺序是不能保证的,但是并不会重排序 1 的顺序,因为 2,3 指令需要依托 1 指令执行结果。最终的执行顺序可能是 1-2-3 也可能是 1-3-2。

1-3-2的情况

上面多线程执行的流程中,如果线程A获取到锁进入创建对象实例,这个时候发生了指令重排序。当线程A 执行到 t3 时刻(singleton已经非null了,但是却没有初始化),此时线程 B 抢占了,由于此时singleton已经不为 Null,会直接返回 singleton对象,然后使用singleton对象,然而该对象还未初始化,就会报错。我们只需将 singleton 变量声明成 volatile 就可以禁止指令重排,避免这种现象发生。

参考/好文:

菜鸟教程 – 设计模式--https://www.runoob.com/design-pattern/singleton-pattern.html

掘金 --https://juejin.im/post/5d54c2d251882542f27bdff6

双重校验锁 --使用volatile和两次判空校验的更多相关文章

  1. Java基础教程:多线程杂谈——双重检查锁与Volatile

    Java基础教程:多线程杂谈——双重检查锁与Volatile 双重检查锁 有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化.此时程序员可能会采用延迟初始化.但要正确实 ...

  2. 对象部分初始化:原理以及验证代码(双重检查锁与volatile相关)

    对象部分初始化:原理以及验证代码(双重检查锁与volatile相关) 对象部分初始化被称为 Partially initialized objects / Partially constructed ...

  3. 单例模式中用volatile和synchronized来满足双重检查锁机制

    背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...

  4. 关于Java单例模式中双重校验锁的实现目的及原理

    开始复习设计模式,一开始理解单例模式中的双重校验锁卡住了,想通了后就自己做了段思维导图来帮助自己理解. 其实理解下来并不难,但还是记录下来帮助自己回忆和借机试试养成写博客的习惯~ public cla ...

  5. 双重检验锁模式为什么要使用volatile?

    并发编程情况下有三个要点:操作的原子性.可见性.有序性. volatile保证了可见性和有序性,但是并不能保证原子性. 首先看一下DCL(双重检验锁)的实现: public class Singlet ...

  6. Java中单例七种写法(懒汉、恶汉、静态内部类、双重检验锁、枚举)

    /*** * 懒汉模式 1 * 可以延迟加载,但线程不安全. * @author admin * */ public class TestSinleton1 { private static Test ...

  7. Java中的双重检查锁(double checked locking)

    最初的代码 在最近的项目中,写出了这样的一段代码 private static SomeClass instance; public SomeClass getInstance() { if (nul ...

  8. 单例模式双重检验锁的判断是否为null的意义

    关于双重检验锁首先简单来看一个小例子: public class Singleton{ private static Singleton instance = null; private Single ...

  9. 双重检查锁实现单例(java)

    单例类在Java开发者中非常常用,但是它给初级开发者们造成了很多挑战.他们所面对的其中一个关键挑战是,怎样确保单例类的行为是单例?也就是说,无论任何原因,如何防止单例类有多个实例.在整个应用生命周期中 ...

随机推荐

  1. Github 美化设置个人主页

    起因是发现自己follow的大师傅个人主页跟普通的不太一样: 猜测应该是Github啥时候出现的新功能,查了一下,发现可以通过创建同名仓库来实现个人主页的美化设置 首先在 GitHub 上建立一个与自 ...

  2. XSS漏洞防御之HttpOnly

    WWW服务依赖于Http协议实现,Http是无状态的协议,所以为了在各个会话之间传递信息,就需要使用Cookie来标记访问者的状态,以便服务器端识别用户信息. Cookie分为内存Cookie和硬盘C ...

  3. python zip()函数用法

    zip() --内建函数 zip([iterable, ...]) 它接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple(元组),然后返回由这些tuples组成的list(列表) ...

  4. js onreadystatechange 和 onload的区别

    IE的script 元素只支持onreadystatechange事件,不支持onload事件. FF的script 元素不支持onreadystatechange事件,只支持onload事件. 如果 ...

  5. 微信小程序中使用text-indent实现首行缩进

    问题由来:在小程序中使用text标签包裹了一段话,要做一个首行缩进的效果,但是不起效果 . 解决方法:使用view标签代替text,使用text-indent:2em即可解决.

  6. typora字体与字体颜色

    字体 基本格式:\字体信息{内容} 罗马体\rm \rm{罗马体abc}>>\(\rm{罗马体abc}\) 意大利体\it \it{意大利体}>>\(\it{意大利体}\) 等 ...

  7. Mysql为什么使用b+树,而不是b树、AVL树或红黑树?

    首先,我们应该考虑一个问题,数据库在磁盘中是怎样存储的?(答案写在下一篇文章中) b树.b+树.AVL树.红黑树的区别很大.虽然都可以提高搜索性能,但是作用方式不同. 通常文件和数据库都存储在磁盘,如 ...

  8. linux修改文件所属的用户组以及用户

    linux修改文件所属的用户组以及用户 将文件夹从A用户(huangxf)目录复制B用户(zhenglf)目录,其中B没有sudo权限.将A的Downloads文件夹下的所有文件,复制到B的Docum ...

  9. 服务器标配 SSH 协议,你了解多少?

    年初,新冠肺炎疫情的出现,全国数千万名员工在家远程办公,使用个人设备通过家庭网络访问公司资料.因此,IT 安全团队面临了众多新挑战:如何实施更加安全的身份验证方案,以确保只有授权人员和设备才能访问公司 ...

  10. Kafka服务器后台启动

    nohup bin/kafka-server-start.sh config/server.properties 1>/dev/null 2>&1 &