一针见血系列[17]: 深度解析volatile禁止指令重排序

栏目:一针见血 作者:admin 日期:2018-11-23 评论:0 点击: 2,901 次

1 指令重排的介绍

在Java内存模型中说过,为了性能优化,编译器和处理器会进行指令重排序;也就是说java程序天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。在单例模式的实现上有一种双重检验锁定的方式(Double-checked Locking)。代码如下:

这里为什么要加volatile了?我们先来分析一下不加volatile的情况,有问题的语句是这条:instance = new Singleton();
这条语句实际上包含了三个操作:
1.分配对象的内存空间;
2.初始化对象;
3.设置instance指向刚分配的内存地址。但由于存在重排序的问题,可能有以下的执行顺序:

如果2和3进行了重排序的话,线程B进行判断if(instance==null)时就会为true,而实际上这个instance并没有初始化成功,显而易见对线程B来说之后的操作就会是错的。而用volatile修饰的话就可以禁止2和3操作重排序,从而避免这种情况。volatile包含禁止指令重排序的语义,其具有有序性。

2 volatile禁止指令重排序的深度分析

volatile除了保证内存可见性,还有个作用是防止指令重排。个人觉得:指令重排是不是最终的思想来源还是内存可见性呢?如果两个互不相关的思想,用到一个事物上,感觉怪怪的。我后来想了想:寄存器和主存的隔离造成了数据的不一致,volatile的初衷是保证数据的强一致性,当赋值基本简单类型的时候,这种一致性很容易实现。但是赋值对象类型的时候,这种一致性分为强一致性和弱一致性,重排是弱一致性,而有序则是强一致性,volatile的目的是强一致性,所以最终它要求指令不得重排。现在我感觉可以把可见性和有序性都统一到一致性上面了。

与本文相关的内容可以看看下面几篇文章:

(1)《双重校验锁,这样理解才是最靠谱的!》
(2)《什么叫内存可见性?什么叫寄存器可见性?》
(3)《volatile离我们很近,很亲切,不该陌生》
(4)《CPU乱序执行是正常,不乱序才是有问题呢!》

一针见血系列[17]: 深度解析volatile禁止指令重排序:等您坐沙发呢!

发表评论