Java的LoadLoad内存屏障可以理解为Load1 happens-before Load2吗?

萧离X1aolone
  • 0
新手上路,请多包涵

比较完整地表述一遍我的问题:Java内存屏障中LoadLoad屏障是否可以理解为,对于Load1; LoadLoad; Load2;这个语句,Load1 happens-before Load2?

最近在看《深入浅出 Java 多线程》的时候,关于volatile关键字有这么一段内容

编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。编译器选择了一个比较保守的JMM内存屏障插入策略,这样可以保证在任何处理器平台,任何程序中都能得到正确的volatile内存语义。这个策略是:

  • 在每个volatile写操作前插入一个StoreStore屏障;
  • 在每个volatile写操作后插入一个StoreLoad屏障;
  • 在每个volatile读操作后插入一个LoadLoad屏障;
  • 在每个volatile读操作后再插入一个LoadStore屏障

大概示意图是这个样子:

image.png

再逐个解释一下这几个屏障。注:下述Load代表读操作,Store代表写操作

  • LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
  • StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,这个屏障会把Store1强制刷新到内存,保证Store1的写入操作对其它处理器可见。
  • LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
  • StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的(冲刷写缓冲器,清空无效化队列)。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能

按照上面这段话,结合happens-before原则,是否可以这么理解:

对于这四个屏障,可以按名字拆分成两个动作,比如LoadLoad,代表两个Load操作,而屏障要做的就是保证第一个Load操作 happens-before 第二个Load操作,其他屏障同理

在网上看到很多博客,都是和书里一样,happens-before单独讲,内存屏障单独讲,没有发现把两者联系在一起理解的内容,所以很好奇,我上面的理解是否有问题?如果有问题的话,在哪方面有问题?

回复
阅读 329
1 个回答

其实我也有点模糊,不过依然可以讨论一下。
先说结论:你可以这么帮助理解,但这并不正确。
happens-before 是一组规则,并不是一种同步的概念,这一点很重要。
happens-before 某个事件先发生(带入现实),这让不相干的线程之间产生了时间上关联,想象在现实世界中,两个线程对应两个人A,B,而变量C代表某个一物品,定义:A修改 happens-before B修改。A 从时间上先对C做了修改,B在修改的时候是一定可以观察到C的修改的,即A修改先行发生于B修改,由于时间有先后,即有可能B先修改,A在修改,结论便不成立 因为并没有定义 B修改 happens-before A修改,所以A 未必能观测到B的修改(这个现实又区别,因为现实中 happens-before 是天然存在的,因为现实并没有副本一说,改动了它就是它),所以这只能是一种偏序关系。而LOADLOAD更加类似于一种全序关系。有点像validate 和 锁的关系。

你知道吗?

宣传栏