读取易失性变量会影响线程缓存中其他非易失性变量的值吗?

Does reading a volatile variable affects the value of other no-volatile variable in thread cache?

为什么下面的代码可以停止线程 1?读取易失性变量会影响线程缓存中其他非易失性变量的值吗?当线程1读到“s”时,它也会重新加载“运行”的新值?

public class Vol {

boolean run = true;
volatile int s = 1;
public static void main(String[] args) throws InterruptedException {

    Vol v = new Vol();
    //thread 1
    new Thread(() ->{
        while (v.run) {
            //with this, thread 1 can be shutdown
            int a = v.s;
        }
    }).start();
    //thread 2
    new Thread(() ->{
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        v.run = false;
        System.out.println("set run false");
    }).start();
}

}

Java 内存模型 (JMM) 定义了一些 gua运行tee。除了这些 gua运行 tees 之外,VM 可以自由地做任何他们想做的事。因此,通常情况下,问题的答案如下:“是否有可能在此处刷新缓存?”是 yes,因为您应该问更多绝对主义的问题:“这里是否刷新了 gua运行teed?”是一个更有趣的问题。

JMM 基于 'Happens-Before/Happens-After' 的概念。 JMM 定义了特定的场景,在这些场景中,JMM 将保证代码将执行,以便您观察到的内容与特定行发生在其他行之前的想法相匹配。 对于未建立 HB/HA 关系的所有 java 代码行,JVM 可能会采取这样的方式,您观察到一个 运行 在另一个之前,或者那个 运行 接一个,甚至是部分可见而其他部分不可见的奇怪组合

换句话说,将其视为一枚邪恶的硬币:没有 HB/HA,JVM 每次交互都会抛硬币(一个线程所做的更改,另一个线程可以观察到吗?没有 HB/HA,邪恶的硬币被翻转)。糟糕的是,赔率不是 50/50,这只是为了惹你生气:在开发和为期一周的测试期间,它每次都能成功,而就在销售人员向大客户提供该演示时,它失败了。

没有简单的方法来测试抛硬币是否正在发生。尽管如此,您的任务是确保永远不会抛出邪恶的硬币。这很难,所以在编写多线程读写同一字段的代码时要格外小心。

volatile 访问 建立 HB/HA 关系。但是,很难知道是哪个方向。

鉴于此代码:

// shared memory:
volatile int x = 0;
/* not volatile */ int y = 0;

// thread A:
y = 10;
x = 20;

// thread B:
if (x == 20 && y != 10) System.out.println("Augh!");

那么你是 gua运行teed: Augh! can never 打印。那是因为如果你读x为20,HB/HA关系gua运行tees线程B中的行运行s在第二行之后A,以及 A 在第二行或之前所做的一切因此保证 运行 被观察到。但是,您完全没有 gua运行tee x 在这里是 20。只是,如果是,那么即使 y 不是 volatile.[=,线程 A 对 y 所做的更改也会被 gua运行teed 观察到23=]

但是,在这段代码中:

// shared state:

/* both non-volatile */ int x = 0, y = 0;

// thread A:
x = 10;
y = 20;

// thread B:
if (y == 20 && x != 10) System.out.println("AUGH!");

那么 JVM 是完全免费的,邪恶的掷硬币正在发生! JVM 可以自由打印 AUGH! 每次,或者从不,或者有时,或者仅在满月时。没有 HB/HA,因此 VM 不会对可观察性做出任何保证,也没有意义。 JVM 实现允许线程 B 观察对 y 的更改而不是对 x 的更改是可以接受的,即使一个简化但错误的概念认为线程实际上 运行 代码顺序会建议这是不可能的。基本上,没有HB/HA,所有赌注都没有。

在您的代码片段中,线程 A 正在读取可变变量 s,但这是唯一与 s 可用的交互;线程 B 从不接触 s.

因此,此代码已损坏,正在发生邪恶的掷硬币! - JVM 可以按如下方式自由工作:线程 2 今天将 run 设置为 false。尽管如此,线程 1 运行 又持续了 3 天,然后,似乎是在一个完全任意的时间,在你没有意识到的情况下,突然间它终于观察到 run 现在是假的。

或者,线程 1 在线程 2 将 run 设置为 false 后几乎立即停止。

要看你的OS,CPU,winamp里放的是什么歌,月相,蝴蝶有没有扇动翅膀。没有办法知道也没有(简单的)方法来测试你是否搞砸了这里。