双重检验锁模式为什么要使用volatile?
并发编程情况下有三个要点:操作的原子性、可见性、有序性。
volatile保证了可见性和有序性,但是并不能保证原子性。
首先看一下DCL(双重检验锁)的实现:
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
无论是volatile修饰singleton 还是 synchronized 还是两次是否未null的判断,都是为了保证 new 操作的正常进行。
所以new操作的背后到底有什么秘密的?
new 实例背后的指令
从字节码可以看到创建一个对象实例,可以分为三步:
- 分配对象内存
- 调用构造器方法,执行初始化
- 将对象引用赋值给变量。
虚拟机实际运行时,以上指令可能发生重排序。以上代码 2,3 可能发生重排序,但是并不会重排序 1 的顺序。也就是说 1 这个指令都需要先执行,因为 2,3 指令需要依托 1 指令执行结果。
虽然重排序并不影响单线程内的执行结果,但是在多线程的环境就带来一些问题。
在JIT里,可能会将2与3进行重排序,在单线程里这里并不会发生什么问题,但是在多线程情况下,会出现下面的问题
时间 |
线程A |
线程B |
t1 |
A1:分配对象的内存空间 |
|
t2 |
A2:设置singleton指向内存空间 |
|
t3 |
B1:判断singleton是否为空 |
|
t4 |
B2:由于singleton不为null,线程B将访问singleton引用的对象 |
|
t5 |
A3:初始化该对象(使得singleton不为空) |
|
t6 |
A4:访问singleton引用的对象 |
A2与A3重排序后,会让线程B在B1处判断出singleton不为null,线程B接下来将访问的singleton引用的对象是一个未初始化的对象。
所以用volatile修饰 singleton来就是禁止2与3的重排序,来保证线程安全的延迟初始化。
参考链接:https://blog.csdn.net/OrangeRawNorthland/article/details/83788412
最新文章
- 每天一个linux命令(33):df 命令
- eclipse中整合springMvc,velocity和sitemesh
- 重放攻击(Replay Attacks)
- WCF多种调用方式兼容
- 用CSS做长度超过长度显示‘...’,当鼠标放上时显示全部内容
- ASP.NET Web API路由规
- centos增加网卡
- 给LinkLabel文本绘制颜色
- spring的配置模式与注解模式基础
- 方格取数(1)(HDU 1565状压dp)
- Libnids---编写网络应用程序的利器
- :after/:before使用技巧
- Python内置函数(60)——compile
- Can not issue data manipulation statements with executeQuery()错误解决
- CloudSim源代码学习——虚拟机(VM)
- [C++基础]队列<;queue>;中的常用函数
- 团队作业8-测试与发布(beta阶段)
- 查询过的问题关于HTML的问题
- excel 妙用选择性粘贴
- Webform和MVC,为什么MVC更好一些?(转)
热门文章
- idea中类注释和方法注释的设置
- StringTable---字符串常量池的垃圾回收跟踪案例
- NodeMCU学习笔记
- WPF 之 INotifyPropertyChanged 接口的使用 (一)
- spring源码学习笔记之容器的基本实现(一)
- Windows下使用poetry和pyproject.toml
- JavaScript里处理数组的一些常用方法
- Codeforces Round #652 (Div. 2) E. DeadLee 贪心
- L3-002 特殊堆栈 (30分) vector容器的模拟、vector容器的一些用法
- springboot demo(一)快速开始