什么样的优化JIT应用于while循环

问题描述 投票:1回答:3

我写下了这段代码:

public class Main {

    private boolean stopThread = false;
    private int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        final Main main = new Main();

        new Thread(() -> {
            try {
                System.out.println("Start");
                Thread.sleep(100);
                System.out.println("Done");
                main.stopThread = true;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

        new Thread(() -> {
            while (!main.stopThread) {
                main.counter++;
            }
            System.out.println(main.counter);
        }).start();
        System.out.println("End");
    }
}

并且当我运行它时,while循环将永远运行。我为此付出了一些努力,并且感到困惑,什么样的优化JIT应用于此代码。

[首先,我认为这是stopThread变量可见性的问题,但是即使它是真的,while循环也应该比我将stopThread分配给true的时间稍晚一些(第一个线程的CPU缓存已刷新到主内存),因此情况并非如此。看起来像JIT硬编码的falsestopThread变量,如果为true,为什么在运行时不以某种方式定期刷新此变量?

很显然,volatile关键字解决了该问题,但是它没有回答我这个问题,因为volatile可以确保可见性并防止JIT达到最佳化的数量。

更重要的是,当我将sleep时间更改为1ms时,第二个线程将正确终止,因此我很确定这与变量可见性无关。

UPDATE:值得一提的是,当counter时间设置为1ms时,我从sleep中获得了非零值。

UPDATE 2:另外,我可以说-XX:+PrintCompilation表示如果将sleep时间设置为100 ms,则会编译while循环,并且会发生On Stack Replacement

UPDATE 3:可能这就是我想要的:https://www.youtube.com/watch?v=ADxUsCkWdbE&feature=youtu.be&t=889。而且我认为-这是JIT进行的“优化”之一,防止这种情况的方法是将变量指定为volatile

java performance jvm jit
3个回答
3
投票

原因不是JIT优化。您的代码正在对共享变量stopThread进行非同步访问(我们称其为“标志”)。

在将标志设置为真实值的线程与正在检查该值的另一​​个线程之间,

基本上存在竞赛条件”。如果在进入循环之前将该标志设置为true,则代码完成。如果没有(种族丢失),则循环将不确定地持续很长时间,因为CPU高速缓存保留了falsey值。当标志为volatile时,将从主存储器而不是CPU缓存中读取其值,并且该标志最终在标志设置线程完成休眠后立即结束循环。


1
投票

我认为您是从错误的方向解决此问题的。

您拥有的程序在内存可见性方面的行为未指定。如果两个线程正在读写共享变量,则必须以特定方式完成此操作,以确保一个线程能看到其他线程的写入。您需要使用synchronizedvolatile变量来确保写入发生在前面读取。

如果<< [happens before不存在,则允许JVM假定只有当前线程正在访问/更新变量。 JIT编译器实际执行的操作取决于各种各样的事情,而进入细节通常没有帮助。

还值得注意的是,volatile不一定是针对内存可见性缺陷的最佳(最有效)解决方案

0
投票
没有任何同步或volatile关键字,允许它不从内存中读取stopThread变量,它可以从第一次放置它的缓存(甚至从寄存器)中读取它。

它到底做了什么以及为什么它与sleep(1)一起工作,我们只能推测。我的猜测是,在第二个线程读取它之前,第一个线程将stopThread = true写入。也许尝试先启动循环线程。

© www.soinside.com 2019 - 2024. All rights reserved.