不理解volatile的传递性是什么意思

static int x = 0;
    volatile static int y = 0;

    public static void main(String[] args) throws InterruptedException {


        new Thread(() -> {
            while (x == 0) {

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            log.debug("改变值");
            x = 1;
            y = 1;
        }).start();
    }

上面代码 y变量加了volatile,x没有,一个线程一直循环判断x的值
另一个线程3s后改变x和y的值,我看好多例子说第二个线程改变x的值由于volatile的传递性,前一个线程对于x的写是可见的,也就是说第一个死循环会结束。
但是实际测试发现不会停下来
这是为什么呢?

亲,你再好好看看volatile你加在x上了,还是y上了...

经过今天的测试 我有点理解了这个传递性的意思了 上面这个例子是不对的 这里我新贴一段代码

 1   static int x = 0;
 2   volatile static int y = 0;
 3
 4   public void actor1(I_Result r) {
 5       while (y == 0) { //B部分 //C部分
 6
 7       }
 8       r.r1 = x + y; //C部分
 9   }
 10
 11   public void actor2(I_Result r) {
 12       x = 1;//A部分
 13       y = 1;//A部分 //B部分
 14   }

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 的防指令重排

分析一下:

  • 1.y的值改变在x之后,不会发生指令重排
  • 2.t2线程改变y=1一定是对t1线程可见的。所以t1线程一定能获取到1
  • 3.根据传递性 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。

分析结束,测试一下,这里我就直接上结果了

img

确实有几率出现x+y=1的情况,说明x的值读取到0的情况。证实了volatile传递性。

如有问题,请指出,我也是刚学习多线程的小白