【Java并发(四)】--Java内存模型之重排序


如未作特殊说明,文章均为原创,转发请注明出处。

前言
java程序中,JMM想尽了各种方法来提高其性能,在软硬结合的情况下,是的JAVA程序能够高效且安全的运行。本文叙述的指令重排序则是JAVA编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

​ 在保证最终结果不受影响的情况下进行指令的重排序来提高程序运行的效率。

​ 指令重排序需要满足以下两个条件:

  1. 在单线程环境下不能改变程序运行的结果;
  2. 存在数据依赖关系的不允许重排序;

总而言之,提高性能的前提就是不能影响程序的最终的执行结果。于上篇博文而言,在JAVA对一段执行指令没有强调happens-before(数据的依赖性)时,证明该段指令可以进行指令重排序的优化。


数据的依耐性

​ 这里相当于强调一下上篇博文所指出的happens-before规则。

​ 如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据的依赖性。数据依赖分为下来3种类型,如下表:

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

这里列举的三种情况,如果发生发生指令重排序,那么结果都会发生变量

发生指令重排序(a b 都有初始值 0) 结果 正确的结果😊
如果第一个操作先是b读取了a的值,再执行a = 1; a = 1 ; b = 0 ; a = 1 ; b = 1 ;
a = 2 执行再 a = 1 之前 a = 1 ; b = 0 ; a = 2 ; b = 0 ;
b = 1执行在a = b 之前 a = 1 ; b = 1 ; a = 0 ; b = 1;

从上述对比可以看出,如果两个操作存在数据的依赖性,那么这两个操作如果发生了指令重排序,就会导致最后执行的结果出错。

​ 所以很明县,JAVA知道这一点,如果数据存在依赖性的话,编译器和处理器是不会对该操作进行重排序的,一定会遵守数据依赖性。

​ 这里所说的数据依赖性仅仅针对于单个处理器中的单个线程中的执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

​ 由此可见:编译器和处理器的重排序发生的唯一前提是不能影响程序的最终执行结果。


as-if-serial语义

​ as-if-serial语义的意思就是:编译器和处理器在提高性能(重排序来提高指令的并行度),单线程中的程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial

​ 由此可以看出,as-if-serial是为了上述数据依赖性提出的规则。

​ 为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,那么这些操作就可能会被编译器和处理器重排序,从而提高程序的执行效率。具体下面的实例解析

int a = 1;         // A
int b = 2;         // B
int c = a + b;     // C

上面程序在单线程中代表着三个指令。


指令关系图

可以很明显的看出来A与C有数据依赖关系 B与C也有数据依赖关系,但是A与B却没有数据依赖关系。

所以A、B两个指令编译器和处理器可以对齐进行重排序,但是A、B两个操作必须发生在C操作之前,编译器和处理器必须遵守as-if-serial语义。

用上篇博文来看待上面的代码的话

A happens-before C

B happens-before C

但是A、B两个操作则没有这种关系。


重排序对多线程的影响

​ 由于在单线程中存在as-if-serial语义,重排序不会对程序的执行结果造成影响,那么在多线程情况下呢?

public class ReorderExample {
    int a = 0;
    boolean flag = false;

    /**
     * A 线程执行
     */
    public void writer() {
        a = 1;              // 1
        flag = true;        // 2
    }

    /**
     * B 线程执行
     */
    public void reader() {
        if (flag) {         // 3
            int i = a * a;  // 4
        }
    }
}

如果此时有两个线程,A线程访问writer()方法,B线程访问reader()方法,那么如果此时B线程访问到了第4步,那么此时的i的值是什么呢?

答案:有可能是1,有可能是0;

因为在writer()方法中1、2并不存在数据依赖性,那么这两个操作有可能被编译器和处理器进行重排序,在B线程读到flag = true时,此时由于发生重排序,A线程先执行的2操作,那么此时B线程时读取不到A线程写入a的数据的。所以这里的得到的结果出乎意料。

线程A发生重排序

这种情况是程序 1、2发生重排序,线程B运行时,正好是线程A给flag赋值,还没有给a赋值的时候,所以线程B不会读取到线程A对a写入的数据。
线程B发生提前预读,重排序

第二种情况的重排序发生可能发生在B线程上,因为as-if-serial语义中定义的是数据存在依赖关系时,不能进行重排序,但是操作3、4之间时控制依赖关系,当代码中存在控制依赖性时,会影响指令序列执行的并行度。为此,编译器和处理器会采用猜测(Speculation)执行来客服控制相关性并行度的影响。以上述为例,在线程B执行if语句时,编译器和处理器可以提前读取并计算a * a并且吧计算记过保存到一个名为重排序缓冲(Reorder Buffer,ROB)的硬件缓冲中。当操作3条件判断为真时,就会把该缓冲的计算结果写入到i中。

​ 从上图可以看出,猜测执行会影响多线程的程序语义。

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


总结

​ 在多线程并发执行的程序中,在但线程中为了提高程序的并行性的重排序,在多线程的情况下,可能会破坏多线程的执行语义。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,321评论 6 543
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,559评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,442评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,835评论 1 317
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,581评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,922评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,931评论 3 447
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,096评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,639评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,374评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,591评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,104评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,789评论 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,196评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,524评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,322评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,554评论 2 379

推荐阅读更多精彩内容