gpt4 book ai didi

java - 需要帮助理解 java 中多线程时的内存可见性问题

转载 作者:行者123 更新时间:2023-12-02 09:28:40 25 4
gpt4 key购买 nike

我正在实践 Goetze 的 Java 并发性,并且在不使用同步关键字时停留在共享变量的内存可见性部分。

代码如下

public class NoVisibility {

private static boolean ready;
private static int number;

private static class ReaderThread extends Thread {
public void run() {
while(!ready)
Thread.yield();
System.out.println(number);
}
}

public static void main(String[] args){
new ReaderThread().start();
number=42;
ready=true;
}
}

作者说这个类可能会永远循环,因为ready的值可能永远不会对读取器线程可见。

我不明白这个说法。

我的看法是,首先主线程启动并将数字和准备设置为 true。但是另一个线程有自己的堆栈以及自己的 number 和 read 值,这些值不与主内存同步,并且这两个线程只有自己的变量副本。

现在,读者线程应该永远保持在循环中。我想知道为什么 Thread.yield 变量不会屈服于主线​​程,然后主线程应该刷新到主内存,然后 readerthread 应该获取这个新值并终止循环,打印正确的值,因为它也应该已同步。

所以我想我有一些问题。

CPU 缓存中的值多久刷新/与主内存同步一次?

该值是否可以不与主存同步,这也是一种可能吗?

为什么会发生这种情况?

当只有一个 cpu 核心和一个 cpu 缓存时,这种内存可见性是否也会发生,还是总是发生?

尽管我了解竞争条件和死锁,但我在理解内存可见性问题时遇到了一些困难。这是架构特定的东西吗?

最佳答案

How often does the value in the cpu's cache get flushed/synced with main memory?

未定义。当 JLS 中指定的可见性保证表明需要发生缓存刷新时,就会发生缓存刷新。

Can the value be not synced with the main memory is that also a possibility?

是的。

Why would this happen?

一般来说,缓存被刷新是有原因的。 happens-before关系表明可能需要刷新缓存的位置。

Does this memory visibility happen also when there is only once cpu core and one cpu cache or does it happen always?

如果只有一个核心,则缓存刷新不是问题1

I am having some trouble understanding the memory visibility problem though I understand race conditions and deadlocks. Is this something architecture specific?

是和否。内存可见性可能表现不同,具体取决于硬件架构以及其他,但是编写代码以提供明确定义的行为的方式是独立于架构的。

如果您确实需要深入了解内存可见性问题,则需要了解内存模型。 Goetz 等人第 16 章中以通俗易懂的方式对其进行了描述,并在 JLS 中进行了详细说明。

<小时/>

I want to know why the Thread.yield() call will not yield to the main thread and then the main thread should flush to the main memory

  1. Thread.yield()可能让出另一个可运行线程。但是,当调用 yield() 时,main 线程很可能不再可运行。 (或者它可能仍在运行。)

  2. yield() 不会在主线程和子线程中的任何语句之间创建 happens-before。如果没有发生在关系,运行时没有义务确保主线程赋值的结果对子线程可见。

  3. 虽然 Thread.yield() 可能执行缓存刷新2,但它将刷新子线程的缓存,而不是父线程的缓存。

因此,子线程的循环可能无限期地继续下去。

<小时/>

1 - 实际上,这可能过于简单化了。例如,在一个具有一个核心和多个具有自己缓存的超线程的系统中,就需要缓存刷新。

2 - 例如,如果 yield() 确实导致上下文切换,则上下文切换通常包括缓存刷新作为操作系统执行的线程状态保存的一部分。但是,yield() 不一定会导致上下文切换。而且JLS并没有明确这方面的规定。

关于java - 需要帮助理解 java 中多线程时的内存可见性问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58153092/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com