考虑摘自 Joshua Bloch 的 Java Concurrency in Practice 一书中的片段 -

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;                            // Statement 1
        ready = true;                           // Statement 2
    }
}

对于 JVM 启动的 线程,是否保证 语句 1 将在语句 2
之前执行

我完全理解 ReaderThread 可能无法看到上述两个静态变量的更新值。我不是在寻求解决方案。但是如果语句 1 在语句 2 之前执行,ReaderThread 是否仍然可以看到 ready 而不是 number 的更新值?这是 重新排序 的一般含义吗?

同一本书页面底部的一段话揭示了对这一点的洞察——



这里有点困惑-

作者是说……只要在该线程中无法检测到重新排序……同时,他说-

- 即使重新排序对其他线程来说是明显的(清晰可见)。

如果以防万一,其他线程可以清楚地看到重新排序,为什么他同时说“只要在该线程内无法检测到重新排序”? 如果重新排序可见,则意味着它也可检测到 。不是吗?

最佳答案

这在一般情况下是不能保证的。此外,不能保证发生更新,因为没有 volatile 添加到字段之一。这将同步线程的缓存,并保证顺序。

(我希望我是对的。)

澄清(我希望)

给定的场景与 jvm 处理的 java 字节码无关。 (通常)编译器不会巧妙地重新排列或乱序解释字节码。它是在具有本地线程缓存的线程中运行的即时编译代码,重复保存公共(public)变量。

volatile 标记字段确保这些公共(public)变量与所有线程同步。当然,只要结果没问题,单个线程可以以任何顺序执行代码。

y = ++x;

下面伪汇编的实际执行
1. move from @x to register1
2. increment register1
3. move from register1 to @x
4. move from register1 to @y
5. synchronize @x and @y

在不同的处理器上可能会有很大的不同。一个或两个变量可能会缓存在线程内存本身中,要么需要写入 far 变量,要么不需要。

当然,可以保证处理相同的线程给出正确的结果。没有人看到,顺序无关紧要:由于内存的原因,4 可能会在 3 之前变成 3 或比 3 快。

如果 3. 和 4. 被 JIT 编译切换,同一个线程不会看到/检测到任何差异,但其他线程可能首先看到 y 的变化。那是没有 volatile

关于java - 在其他/s之前的指令/语句是否保证首先执行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38694206/

10-16 15:01