保证可见性 之 禁止重排序

所属文集:一起掌握并发

了解重排序

为了性能,编译时和运行时都会有重排序,造成指令执行顺序变了,宏观上从这3点了解重排序:

  1. 线程内有序:如果再本线程内观察,所有的操作都是有序的,即线程内表现为串行的语义(Within Thread As-If-Serial Semantics)。
  2. 线程间无序:如果再一个线程中观察另一个线程,所有的操作都是无序的,即 指指令重排序现象和工作内存与主内存同步延迟现象。
  3. 总会有重排序:指令重排序在任何时候都有可能发生,与是否为多线程无关,之所以在单线程下感觉没有发生重排序,是因为线程内表现为串行的语义的存在。
引起可见性问题的病因

修复Java内存模型,第2部分里的一段话开始思考

理解JMM所需的关键概念之一是可见性 -您如何知道如果线程A执行someVariable = 3,其他线程将看到线程A在其中 写入的值3?存在许多原因,为什么另一个线程可能不会立即为以下值看到值3 someVariable:可能是因为编译器已对指令进行了重新排序以便更有效地执行,或者已将someVariable其缓存在寄存器中,或者将其值写入了缓存在写处理器上但尚未刷新到主内存,或者在读处理器的缓存中有旧的(或陈旧的)值。内存模型确定线程何时可以可靠地“看到”对其他线程所做的变量的写入。特别是,内存模型为定义了语义volatile,synchronized,final这确保了跨线程的内存操作可见性。

总结为:导致可见性的原因有很多

  1. 为了提升性能而实施的编译期重排序。
  2. 数据在寄存器中。
  3. cpu缓存的更改未同步到主内存中 或 内存中的更改未同步到cpu缓存(运行期重排序)。
  4. 。。。

这里两次提到重排序,那就先看重排序。

重排序 遵守 as-if-serial语义

as-if-serial语义 : 不管怎么重排序(编译器和处理器为了提高并行度),程序在单线程中的执行结果不会改变。

编译器、runtime和处理器都必须遵守as-if-serial语义:

  1. 为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。

  2. 如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序

正确的理解as-if-serial语义的功效:

  1. 如果再本线程内观察,所有的操作都是有序的;
  2. 如果再一个线程中观察另一个线程,所有的操作都是无序的。
重排序的类型:

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:

  1. 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。属于编译期重排序。

  2. 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。属于运行期重排序。

  3. 内存系统的重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行,属于运行期重排序。

image.png

从编译运行视角分为两类:

  1. 编译期重排序:包括 编译器优化的重排序。
  2. 运行期重排序:包括 指令级并行的重排序,内存系统的重排序。

理解什么叫运行期重排序

image.png

解读上图:已变更的数据立即写到内存太慢,所以先写到Store Buffer.
举个例子,厨师饭做好了,不会直接端给你,而是放到那里等服务员端给你,无论服务员什么时候端给你,都不算是做好了直接给你吃,而是放置了一会儿。

image.png

解读上图:其他cpu通知说,我缓存的数据无效了,但是我在忙别的,不想打断正在做的事情,于是提供了一个通知队列,让他们把缓存无效的通知先放到 通知队列中,等我忙完了再去处理通知

store buffer

  1. 优点:
  • 可以保证core内的指令流水线持续运行,
  • 它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。
  • 通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,可以减少对内存总线的占用。
  1. 缺点:
    每个处理器上的写缓冲区,仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的写操作的执行顺序,不一定与内存实际发生的写操作顺序一致!

Invalidate Queue

  1. 优点:
    正在处理的事情不中断
  2. 缺点:
    处理器对内存的读操作的执行顺序,不一定与内存实际发生的写操作顺序一致!使用已经过期的数据,而不是最新的。

在两个CPU同时运行的情况下,CPU0自身视角来说,没有重排发生,一切都那么自然,但是CPU1却看到CPU0发生了重排(reordering memory)。这就是内存系统重排序。

禁止运行期重排序

store bufferInvalidate Queue 带来的乱序如何解决

CPU通常提供了内存屏障指令,来解决这样的乱序问题。读屏障,清空本地的invalidate queue,保证之前的所有load都已经生效;写屏障,清空本地的store buffer,使得之前的所有store操作都生效。

通俗来说就是两点:

写屏障:保证把更新写到内存
读屏障:保证从内存读取最新数据

JMM把内存屏障分为四类,其实就是读、写屏障的组合:

image.png

JMM 的处理器重排序规则 会 要求 java 编译器在生成指令序列时,插入特定类型的内存屏障指令,来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。

理解什么叫编译期重排序:
private int val = 0;
private volatile boolean stop = false;
...
fun(){
val = 10;//代码1
stop = true;//代码2
}

这里代码1 和代码2 编译后顺序不会调整很容易理解,但是下边这个例子就有歧义了:

private boolean stop ;
while(!stop){
  i++;
}

按照java中volatile关键字的疑惑大湿的解答
这种代码会被编译优化成类似

if(!stop){
     while(true){
          i++;
    }
}

这种答案我还是不太信服的,看官您若有答案请留言告知哦。
大师的答案中用到了HSDIS技术,深入解析volatile关键字 对HSDIS的讲解很全面

禁止编译期重排序

JMM 的编译器重排序规则 会 禁止 特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。

java中禁止重排序的操作

  1. volatile关键字

  2. unsafe 的内存屏障方法

  3. synchronized锁

题外话之总线风暴

总线风暴:总线带宽达到峰值;原因:

  1. 内存屏障从主内存嗅探
  2. cas不断循环无效交互导致

解决办法:
部分volatile和cas使用synchronize

Happens-Before 规则 关注使用效果而非实现。

上述内容应该也只是保证可见性的一部分内容,我本身还困惑寄存器缓存和指令并行重排等情况,作为上层高级语言程序员,很难很难掌握全部底层的情况。

通过一种更简单的方式,结合 java语法,以使用的视角来掌握如何保证可见性,而不是 如何实现可见性保证。很自然的happens-before的概念就是这个作用,告诉程序员怎么用。 而保证可见性的实现则是JMM自己的事情,不是程序员的。

image.png

从 JDK5 开始 java 使用新的 JSR -133 内存模型,并依据此内存模型提出了 happens-before 的概念,通过这个概念来阐述操作之间的内存可见性。

我们要保证可见性,就是遵守Happens-Before 规则,合理的使用java提供的工具。


为什么会有内存屏障

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

推荐阅读更多精彩内容