System.out 神秘消失,尽管代码/行执行

问题描述

好的,我已经在 java 8、11 和 14 上测试了这段代码,它们都有相同的结果。 这是不好的做法,也是一个不切实际的场景,但我想了解导致这种情况发生的 JVM 内部原理。

如果你运行这段代码,你会注意到除了 system.out.println 里面的打印部分本身之外的所有东西都执行了。 在某些时候使用稍微不同的 Java 版本,我设法通过将“播放”更改得太不稳定来打印它,但即使这样现在也不起作用。

请至少在声称它只是使变量死锁或使用缓存之前测试代码,事实并非如此,if 执行并且其中的所有内容都可以工作,除了打印部分本身。

public class Main {
    public static void main(String[] args) {
        TestClass t = new TestClass();
        System.out.println("Starting test");
        new MyRunnable(t).start();
        while (true)
            t.testUpdate(System.currentTimeMillis());
    }
}
public class MyRunnable extends Thread {

    private TestClass t;

    public MyRunnable(TestClass t) {
        this.t = t;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(500L);
            t.setPlay(true);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class TestClass {
    private boolean play = false;
    private long lastUpdate = 0;
    private long updateRate = 2000;
    private boolean hasSysoBeenHit = false;

    public void testUpdate(long callTime) {
        System.out.println(play);
        System.out.println((callTime-lastUpdate));
        if (this.play && ((callTime-lastUpdate) >= updateRate)) {
            System.out.println("Updating! " + (hasSysoBeenHit = true));

            this.lastUpdate = callTime;
        }
        System.out.println("hasbeenhit? " + hasSysoBeenHit);
    }

    public void setPlay(boolean t) {
        System.out.println("Starting game...");
        this.play = t;
    }
}

解决方法

您的代码在 TestClass.play 字段上遇到数据竞争:有 2 个线程访问此字段,其中至少有一个执行写入操作。 @aerus 已经表明了这一点。

如果您使字段变得易变,则数据竞争将被消除。在 Java 内存模型中查找 volatile 变量规则。

我还将播放检查的逻辑移到 testUpdate 方法的开头:

public void testUpdate(long callTime) {
    if(!play)return;
    ... 

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...