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

为什么在同步代码块内还要再检验一次?
第一个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和两次判空校验的更多相关文章
- Java基础教程:多线程杂谈——双重检查锁与Volatile
Java基础教程:多线程杂谈——双重检查锁与Volatile 双重检查锁 有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化.此时程序员可能会采用延迟初始化.但要正确实 ...
- 对象部分初始化:原理以及验证代码(双重检查锁与volatile相关)
对象部分初始化:原理以及验证代码(双重检查锁与volatile相关) 对象部分初始化被称为 Partially initialized objects / Partially constructed ...
- 单例模式中用volatile和synchronized来满足双重检查锁机制
背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...
- 关于Java单例模式中双重校验锁的实现目的及原理
开始复习设计模式,一开始理解单例模式中的双重校验锁卡住了,想通了后就自己做了段思维导图来帮助自己理解. 其实理解下来并不难,但还是记录下来帮助自己回忆和借机试试养成写博客的习惯~ public cla ...
- 双重检验锁模式为什么要使用volatile?
并发编程情况下有三个要点:操作的原子性.可见性.有序性. volatile保证了可见性和有序性,但是并不能保证原子性. 首先看一下DCL(双重检验锁)的实现: public class Singlet ...
- Java中单例七种写法(懒汉、恶汉、静态内部类、双重检验锁、枚举)
/*** * 懒汉模式 1 * 可以延迟加载,但线程不安全. * @author admin * */ public class TestSinleton1 { private static Test ...
- Java中的双重检查锁(double checked locking)
最初的代码 在最近的项目中,写出了这样的一段代码 private static SomeClass instance; public SomeClass getInstance() { if (nul ...
- 单例模式双重检验锁的判断是否为null的意义
关于双重检验锁首先简单来看一个小例子: public class Singleton{ private static Singleton instance = null; private Single ...
- 双重检查锁实现单例(java)
单例类在Java开发者中非常常用,但是它给初级开发者们造成了很多挑战.他们所面对的其中一个关键挑战是,怎样确保单例类的行为是单例?也就是说,无论任何原因,如何防止单例类有多个实例.在整个应用生命周期中 ...
随机推荐
- C语言网络编程(Linux && Windows)(1)
和朋友一起做课程设计,同时学习C语言的网络编程,以前写的都是python网络编程,但python很多的库都是封装好的,大部分人在使用的时候不会去了解底层的实现,这样对长远的学习不太好,也改正自己这方面 ...
- 刷题记录:[GWCTF 2019]枯燥的抽奖
目录 刷题记录:[GWCTF 2019]枯燥的抽奖 知识点 php伪随机性 刷题记录:[GWCTF 2019]枯燥的抽奖 题目复现链接:https://buuoj.cn/challenges 参考链接 ...
- 2、tensorflow 变量的初始化
https://blog.csdn.net/mzpmzk/article/details/78636137 关于张量tensor的介绍 import tensorflow as tf import n ...
- 使用.Net Core做个爬虫
最近接手一个新项目,爬亚马逊分类.商品数据.记得大学的时候,自己瞎玩,写过一个爬有缘网数据的程序,那个时候没有考虑那么多,写的还是单线程,因为网站没有反爬,就不停的一直请求,记得放到实验室电脑上一天, ...
- 【学习笔记】浅析平衡树套线段树 & 带插入区间K小值
常见的树套树 一般来说,在嵌套数据结构中,线段树多被作为外层结构使用. 但线段树毕竟是 静态 的结构,导致了一些不便. 下面是一个难以维护的例子: 带插入区间 \(k\) 小值问题 来源:Luogu ...
- 【SPOJ QTREE4】Query on a tree IV(树链剖分)
Description 给出一棵边带权(\(c\))的节点数量为 \(n\) 的树,初始树上所有节点都是白色.有两种操作: C x,改变节点 \(x\) 的颜色,即白变黑,黑变白. A,询问树中最远的 ...
- 把java编译成exe和安装包
由于某些项目甲方迟迟不结算尾款,这就很烦,只能想一些办法 我们知道java,python之类的代码是没有隐私可言的,那么怎么办,总要发给甲方验收,这就要做一些操作来确保自己的利益. 通过在源代码里加上 ...
- python最大几个数和最小几个数(堆排序)
最大几个数和最小几个数 import heapq a = [7, 5, 3, 4, 8, 6, 0] cc = heapq.nsmallest(2, a) #最小的两个数 dd = heapq.nla ...
- springmvc使用路径变量后再进行页面跳转会出现路径错误问题
学习<Servlet.JSP和SpringMVC学习指南>遇到的一个问题,记录下. 项目代码 现象 @RequestMapping(value = "/book_edit/{id ...
- Spring MVC整合 freemarker
1.什么是Spring MVC? Spring MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将Web层进行职责解耦,基于请求驱 ...