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

我正在实践 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变量不会屈服于主线程,然后主线程应该刷新到主内存,然后读取线程应该拾取这个新值并终止循环并打印正确的值,因为这也应该是已同步。


所以我想我有一些问题。


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


值可以不与主存同步吗?这也是可能的吗?


为什么会出现这种情况呢?


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


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


波斯汪
浏览 79回答 2
2回答

慕村225694

CPU 缓存中的值多久与主内存刷新/同步一次?不明确的。当 JLS 中指定的可见性保证表明需要发生缓存刷新时,就会发生缓存刷新。值可以不与主存同步吗?这也是可能的吗?是的。为什么会出现这种情况呢?一般来说,缓存被刷新是有原因的。发生之前关系指示可能需要缓存刷新的位置。当只有一个 cpu 核心和一个 cpu 缓存时,这种内存可见性是否也会发生,还是总是发生?如果只有一个核心,则缓存刷新不是问题1。尽管我了解竞争条件和死锁,但我在理解内存可见性问题时遇到了一些困难。这是架构特定的东西吗?是和不是。内存可见性可能会根据硬件架构的不同而有所不同,但编写代码以提供明确定义的行为的方式是独立于架构的。如果您确实需要深入了解内存可见性问题,则需要了解内存模型。Goetz 等人的第 16 章中以通俗易懂的方式对其进行了描述,并在 JLS 中进行了详细说明。我想知道为什么Thread.yield()调用不会屈服于主线程,然后主线程应该刷新到主内存可能Thread.yield() 会屈服于另一个可运行的线程。然而,当yield()调用时,线程很可能main不再可运行。(或者它可能仍在运行。)不会 在主线程和子线程中的任何语句之间yield()创建发生之前。如果没有发生之前关系,运行时就没有义务确保主线程分配的结果对子线程可见。虽然Thread.yield() 可能会执行缓存刷新2,但它将刷新子线程的缓存,而不是父线程的缓存。因此,子线程的循环可能会无限期地继续下去。1 - 实际上,这可能过于简单化了。例如,在一个具有一个核心和多个具有自己的缓存的超线程的系统中,将需要缓存刷新。2 - 例如,如果yield() 确实导致上下文切换,则上下文切换通常包括缓存刷新,作为操作系统执行的线程状态保存的一部分。但是,yield()不一定会导致上下文切换。此外,JLS 并没有规定这方面。

慕妹3242003

字段可见性意味着线程观察者字段值来自高速缓存,并且可以具有与 CPU 另一个核心中的其他高速缓存不同的状态。JVM 不保证访问共享资源的不同线程的字段可见性,程序员需要使用 synchronized 来防止读取不正确的状态,或使用 volatile 来保证更改被刷新到其他缓存。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java