【问题标题】:System.out mysteriously disappears, although the code/line executesSystem.out 神秘地消失了,尽管代码/行执行
【发布时间】:2021-01-19 06:56:54
【问题描述】:

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

如果您运行此代码,您会注意到除了 system.out.println 的打印部分本身之外的所有内容如果执行。 在某些时候,使用稍微不同的 java 版本,我设法通过改变“play”太不稳定来打印它,但即使这样现在也不起作用。

请至少在声称它只是使变量死锁或使用缓存之前测试代码,事实并非如此,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;
    }
}

【问题讨论】:

  • 在互联网上搜索可以很容易地解释这一点。好吧,也许并不容易,但你会很快找到原因。这是因为 JVM 正在收集线程的 println 输出。没有办法解决这个问题,这只是 JVM 的实现方式。如果你想这样做,你需要一种不同的方式来获取数据报告。看看stackoverflow.com/questions/9467759/…
  • 什么打印部分不执行?我认为没有问题,除了 play 确实需要是 volatile
  • 怎么只有“更新!”在这种情况下被跳过?其他所有内容都以该方法继续打印,所有打印都来自同一个线程,除了一个。它没有过时,也没有明确共享缓冲区,因为其他一切都有效。还有一个事实,为什么它必须是易变的打印(即使不再对我有用了?)是我所追求的。

标签: java shortcode internals


【解决方案1】:

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

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

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

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

【讨论】:

  • 您是否测试过代码...?而“改善”这不是这个问题的重点。无论如何,不​​应该存在一个完整的 while(true) 无限地打勾游戏。我知道 volatile 是什么,但是如果您实际运行此代码,您会发现并不是 if() 无法访问它。实际上,另一个线程根本只访问它一次,500ms 之后更不用说打印播放,时间和“hasbeenhit”在 500ms 后都打印正常,打印中的副作用 (Updating + (hassysobeenhit = true)) 变成布尔值真的,你可以看到它。仍然没有“更新!”虽然打印。
猜你喜欢
  • 1970-01-01
  • 2018-08-18
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
  • 2021-12-11
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多