16_03_Happen-Before原则的借助
Happen-Before原则的借助
Happen-Before 原则可以保证变量的可见性,因此可以通过 “借助” 的方式,来间接地实现对无锁保护变量的安全性保护。
“借助” 技术,一般将程序次序规则和其他某个规则(比如监视器锁规则或者 volatile 变量规则)结合起来实现的。
这种 “借助” 技术,对于代码语句的顺序非常敏感,因此很容易出错,属于一种高级技术。
举个例子来说明一下这个技术。
一、有并发问题的例子
1 | class VolatileTest { |
线程 B 读到的值可能是什么呢?
(1) 首先,编译出来的指令可能会重排序优化
那么,线程 A 执行的指令代码可能是这样的:
1 | public void writer() { |
也就是说,线程 A 可能先执行 ready = true,然后再执行 x = 1。
(2) 其次,线程 B 对于线程 A 的“影响”存在可见性问题
那么,线程 B 看到的情况可能有几种:
- 没看到 ready = true(可见性问题)
- 看到了 ready = true,但是没有看到 x = 1(有序性问题)
- 看到了 ready = true,也看到了 x = 1
理论上来说,只有第三种情况,才是正确的,但结果未必是这样的。
所以,由于存在有序性问题和可见性问题,线程 B 读到的 x 值是未知的。
二、“借助”技术的可见性保证
这里,只对上面例子中的 ready 加了 volatile 修饰。
1 | class VolatileTest { |
那么线程 B 看到的值都是什么?
(1) 程序次序规则
首先,volatile 变量具有禁止指令重排序的语义,可以保持有序性。
那么在线程 A 中,操作 x = 1 就只能在操作 ready = true 之前执行。
依据程序顺序规则,操作 x = 1
先于操作 ready = true
。
(2) volatile 变量规则
其次,对于 volatile 变量 ready,线程 A 写操作在线程 B 的读操作之前执行
依据 volatile 变量规则,操作 ready = true
也先于操作 ready == true
。
(3) 传递性规则
最后,依据传递性规则,操作 x = 1
必然先于操作 ready == true
。
所以,线程 B 执行到 ready == true
时,必然可以看到 x = 1
。
总结
通过间接地利用 Happen-Before 原则,可以实现无锁保护变量的可见性。
不过这种技术,需要很精确地控制操作的执行顺序,一不小心就很容易写错。
16_03_Happen-Before原则的借助
http://example.com/lang/java/core/concurrency/16_03_Happen-Before原则的借助/