先上代码
static int x = 0;
volatile static int y = 0;
public void actor1(I_Result r) {
while (y == 0) { //B部分 //C部分
}
r.r1 = x + y; //C部分
}
public void actor2(I_Result r) {
x = 1;//A部分
y = 1;//A部分 //B部分
}
x变量无volatile修饰
y变量有volatile修饰
这里,我把这段代码重点标记三个部分
A部分:12行+13行
B部分:13行+5行
C部分:5行+8行
假设线程t1 执行actor1方法,线程t2,执行actor2方法
先看A部分,由于y变量有volatile修饰,所以A部分的12行和13行不会发生指令重排
再看B部分,由于y变量有volatile修饰,第5行的while循环在y的值改变后一定会读取到最新的1,导致while循环结束。
再看C部分,当程序运行到第8行,获取x+y的结果时,这个结果会是几?
问题已经有了,我们先分析 首先这个y一定是1,因为程序能执行到第8行时,y的值必定不是0了。这是肯定的。那么现在的问题就是x是几
结果是x一定是1
原因就是 volatile具有传递性,如果 x hb-> y 并且 y hb-> z 那么有 x hb-> z ,配合 volatile 的防指令重排
分析一下:
- y的值改变在x之后,不会发生指令重排
- t2线程改变y=1一定是对t1线程可见的。所以t1线程一定能获取到1
根据传递性
A部分12行,13行代码符合 x(12行) hb-> y(13行)
B部分13行,5行代码符合 y(13行) hb-> z(5行)
C部分5行,8行代码就一定符合 x(5行) hb-> z(8行)
也就是说当t2的y=1对t1线程可见,那么t2的x=1也一定对t1可见,所以x+y结果一定是2。
但是 如果把actor2方法x,y的赋值顺序颠倒
public void actor2(I_Result r) {
y = 1;//A部分 //B部分
x = 1;//A部分
}
A部分就不符合 x hb-> y ,传递性被破坏,那么C部分读取的x的值就有可能是0。
分析结束,测试一下,这里我就直接上结果了
确实有几率出现x+y=1的情况,说明x的值读取到0的情况。证实了volatile传递性。
如有问题,请指出,我也是刚学习多线程的小白