第三章 Java内存模型之重排序

作者 : 开心源码 本文共2012个字,预计阅读时间需要6分钟 发布时间: 2022-05-12 共120人阅读

接上一章Java内存模型之基础,我们接着探索Java内存模型。我们在上一章已经接触过重排序了,但是还没有那么透彻,这章重点来说下一下重排序。

定义:
重排序是编译器和解决器为了优化程序性能而对指令序列进行重新排序的一种手段。

我们需要了解这几点:
1)数据依赖性
2)as-if-serial
3)程序顺序规则
4)重排序对多线程的影响

数据依赖性:

假如两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。

名称代码实例说明
写后读a=1;b=a写一个变量之后,再读这个变量
写后写a=1;a=2写一个变量之后,再写这个变量
读后写a=b;b=1读一个变量之后,再写这个变量

上面的几个情况,只需重排序都会影响最后的执行结果,只需两个操作有写的操作,重排序就会有影响。当编译器和解决器进行重排序的时候会遵照数据依赖性,编译器和解决器不会改变存在数据依赖性关系的两个操作的执行顺序。

数据依赖性仅仅针对单个解决器,不同解决器不予考虑,想管也管不动啊。

as-if-serial语句:

无论你怎样重排序(编译器和解决器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime和解决器都要遵守as-if-serial语义。

为了遵守as-if-serial语义,编译器和解决器不会对存在数据依赖关系的操作做重排序。由于重排序会改变执行的结果。但是假如操作之间不存在数据依赖关系,那么这些操作即可能被重排序。

int a = 1;//Aint b = 2;//Bint c = a+b;//C

依赖关系如下

当改变 A、B的执行顺序不会对执行结果有影响。

总结一下as-if-serial:
as-if-serial语义把单线程程序保护了起来,遵守as-if-serial的编译器,runtime和解决器共同为编写单线程程序的程序员一个幻觉(合着这么多年被欺骗了啊):单线程程序是按照程序的顺序来执行的。as-if-serail语义是单线程程序员无需担心重排序干扰他们,也无需担心内存的可见性问题。

程序顺序规则:
这个在上一章就已经讲过了,我们再来一下划重点。
假如A happens-before B,实际B可以在A之前执行,JMM不肯定要A肯定要在B之前执行。仅仅是要求上一个操作(结果)对于下一个操作可见,且前一个操作按顺序排在第二个操作之前。

这个例子A的执行结果不需要对操作B可见,而且重排序之后对执行结果无影响,这种情况下,JMM会认为重排序并不非法(not illegal),JMM允许这种重排序。由于计算机技术和硬件技术有着一个共同的目标,在不改变执行结果的前提下,尽可能提高并行效率。

重排序对多线程的影响

最后我们看一下重排序对多线程的影响。说了这么多单线程的,我们来看一下多线程中的体现。

class demo{int a = 0;boolean flag = false;public void writer(){a = 1;//1flag = true;//2}public void reader(){if(flag){//3int i = a*a;//4}

flag 是个标记,表示变量a能否被写入。假如有两个线程A和B,A首先执行writer()方法,随后B执行reader()方法。在操作4的时候,是否看到线程A在操作1对共享变量a的写入呢?
答案:不肯定!!!
其实通过我们前面知识的了解,尽管单线程可以根据规则去判断能否重排序,但是对多线程是无效的,我们从单线程的关系来看是允许进行重排序的,那么就是说,1、2 和 3、4 的顺序可以任意交换。而且1、2 和3、4的顺序也是未知。
首先一种可能的情况 2 、3、 4、1。

1、2重排序

我们发现 a还没被写入,flag就被赋值了,1、2重排序,导致B线程的结果是错误的。这就是重排序把多线程程序的语义破坏了。

我们再看另一种情况3、4重排序。结果也是错误的。重排序把多线程程序的语义破坏了。编译器和解决器会采用猜测(Speculation)执行来客服控制相关性对并行度的影响。以解决器的猜测执行为例子,执行线程B的解决器可以提前读取并计算a*a,而后把这个计算结果临时保存到一个名为重排序缓冲(Reorder Buffer,ROB)的硬件缓存中,当操作3为真,把计算结果写入到变量中。

3、4重排序

也就是猜测执行实际上对操作3和操作4进行了重排序。

结论:
在单线程程序中,对存在控制依赖的操作重排序,不会改变执行结果(as-if-serial语义允许存在控制依赖的操作做重排序的起因)。但是在多线程程序中,对存在控制依赖的操作重排序,可能就会改变程序的执行结果。

换一句话就是 单线程中 对于控制依赖,编译器和解决器已经处理了重排序的问题所以不会影响最后执行结果。

注意这两种依赖:
1)数据依赖(在单线程重排序可能会影响执行结果,在多线程更是可以影响执行结果)
2)控制依赖(运用猜测、 缓存 ,重排序 在单线程不影响执行结果,在多线程可能会影响执行结果 )

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 第三章 Java内存模型之重排序

发表回复