【问题标题】:Understanding Java memory model during context switches在上下文切换期间了解 Java 内存模型
【发布时间】:2021-04-03 17:37:33
【问题描述】:

最近在一次会议演讲中,以下面的例子来演示多线程环境中的 Java 内存模型。

public class A {

    public static boolean done;

    public static void main(String[] args) throws InterruptedException {
        done = false;
        new Thread(new Runnable(){
            public void run() {
                System.out.println("running...");

                int count = 0;
                while (!done) {
                    count++;
                }

                System.out.println("Exiting thread");
            }
        }).start();

        System.out.println("in main...");
        Thread.sleep(2000);

        System.out.println("setting done to true");
        done = true;
    }
}

我知道上面代码中创建的新线程永远不会退出,因为done 变量被缓存在线程的本地缓存中。一个适当的解决方案是使done 变量可变。

但是如果在while循环中,我们调用Thread.sleep()如下

    while (!done) {
        count++;
        try {Thread.sleep(0);} catch(Exception e){}
    }

然后线程成功退出。

我的理解是,由于sleep(0) 会发生上下文切换,这将使缓存条目无效,因此每次检索done 的更新值。我的理解正确吗?这种行为是否也取决于机器的核心数量?

【问题讨论】:

  • 可能发生线程永远不会完成或者它可能。这就是重点:在内存模型中,您无法保证一种或另一种方式。某些特定操作可能会导致从主内存重新读取done,但这完全取决于实现(当然在指定的读取障碍之外)。

标签: java multithreading java-memory-model


【解决方案1】:

Java 语言规范 明确指出 Thread::sleep 没有任何 happens-before 语义(只有这些语义是您要推理的):

... Thread.sleep 和 Thread.yield 有任何同步语义...

因此,您“碰巧”看到使用它的任何输出,都不受规范的保证。您拥有的唯一保证是完成后volatile,同样,因为JLS 为您提供了这样的保证。

您关于正确同步程序的推理必须以 happens-before 为依据,上下文切换、缓存等无关紧要。

【讨论】:

  • 通过说上下文切换和缓存在给定的场景中是不相关的,你的意思是它依赖于实现,所以不应该依赖它,还是你的意思是它与讨论的主题完全无关?
  • @JavaLearner 第一个。
猜你喜欢
  • 2021-06-05
  • 2011-06-03
  • 1970-01-01
  • 2015-09-25
  • 2020-12-13
  • 2012-09-19
  • 1970-01-01
  • 2012-05-09
相关资源
最近更新 更多