并发编程(二十九)内存屏障 happens-before 和 as-if-serial

193 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情

1.内存屏障

image.png

StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他3个屏障的效果。执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(Buffer Fully Flush)。

2.as-if-serial

as-if-serial语义的意思是:不管如何重排序(编译器优化的重排序、指令级并行的重排序、内存系统的重排序),在单线程中程序执行的最终结果不能改变。编译器、runtime和处理器都必须遵守as-if-serial语义。

往往这种as-if-serial语义是不会对存在数据依赖重排序,因为数据依赖进行重排序可能会导致数据结果的不正确。

3.happens-before

happens-before主要是针对的多线程并发情况,阐述内存之间的可见性问题。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。

示例代码:

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

上面加法计算示例代码存在3个happens-before关系,如下。

·A happens-before B。

·B happens-before C。

·A happens-before C。

在3个happens-before关系中,2和3是必需的,但1是不必要的。 A,B可以进行重排序(并行执行)。这是因为语句A和语句B没有依赖关系,所以重排序不会影响最后第三句的结果。

happens-before有以下6个主要的规则:

1)程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。

2)监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。

3)volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

4)传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。

5)start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。

6)join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。