为什么这个print语句改变了这个循环的执行方式?

时间:2015-05-05 17:50:18

标签: java game-loop

这是我的游戏循环线程中的run方法(另一个SO答案的修改版本)。运行布尔值基本上是一种暂停和取消暂停游戏的方法,它通常正常工作。我试图添加一个构造函数,让游戏开始时暂停,或者运行== false并且它不起作用。尽管成功将运行设置为true,但游戏永远不会启动。我添加了一些打印语句来弄清楚发生了什么。然后发生了一件奇怪的事情。添加print语句解决了问题。

private void reset() {
    initialTime = System.nanoTime();
    deltaU = 0; deltaF = 0;
    frames = 0; ticks = 0;
    timer = System.currentTimeMillis();
}
public void run() {
    Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

    reset();
    boolean wasRunning=false;
    while (true) {
        System.out.println("in loop");
        if (running) {
            System.out.println("running");
            if(!wasRunning){
                wasRunning=true;
                reset();
            }
            long currentTime = System.nanoTime();
            deltaU += (currentTime - initialTime) / timeU;
            deltaF += (currentTime - initialTime) / timeF;
            initialTime = currentTime;

            if (deltaU >= 1) {
                update();
                ticks++;
                deltaU--;
            }

            if (deltaF >= 1) {
                render();
                frames++;
                deltaF--;
            }

            if (System.currentTimeMillis() - timer > 1000) { //prints warning if dropped frames
                if (printTime) {
                    System.out.println(String.format("UPS: %s, FPS: %s", ticks, frames));
                }
                if(ticks!=60 || frames!=60)
                    System.out.println(String.format("UPS: %s, FPS: %s", ticks, frames));
                frames = 0;
                ticks = 0;
                timer += 1000;
            }
        }
        else
            wasRunning=false;
    }
}

输出

in loop
running
in loop
running
in loop
running...

游戏中运行了大量的游戏(尽管fps警告奇怪地保持沉默)。当我注释掉System.out.println(“in loop”)时,没有输出,设置运行为true什么都不做。

因此,存在print语句的事实实际上导致循环执行,但省略它会导致它失败。

请注意,如果首先将running设置为true,则会删除print语句,暂停和取消暂停功能将按预期工作。

我还尝试重命名运行到running2,以确保它不会覆盖任何内容。

1 个答案:

答案 0 :(得分:4)

tl; dr :您应该尝试将running标记为volatile

我假设running是从另一个线程设置的?如果是这种情况,如果running最初为false并且仅由另一个帖子设置为true,那么它可能是竞争条件。

println()语句之前添加if调用会为其他线程添加足够的时间,以便在检查之前将running修改为true。删除println()调用会导致running被检查,然后其他线程才能将其设置为true

如果是这种情况,你真的应该重构你的代码以避免这种竞争条件。无法看到这一切,很难就你如何处理这个问题提出建议。

编辑:在阅读下面的评论后,它看起来像无限的while循环会#34; catch"对running的更改,但如果running未标记为volatile,情况可能并非如此。

volatile已经在Java中定义了语义,其中之一是变量永远不会被线程本地缓存,即对volatile变量的访问总是进入主内存,并且总是反映当前状态,即使由其他线程更新。

请参阅volatile关键字上的here for more information

编辑:添加了tl;博士

编辑:快速/外行对volatile的解释: JVM在提高效率方面的优化数量方面可能非常核心。

对于方法中的非易失性字段访问,JVM可以假设其他线程没有访问它;在这样做时,它可以创建该字段的线程本地副本,以便它可以修改它而无需处理访问"主存储器"的争用。这会提高性能,但也有我们已经看到的缺点:如果另一个线程修改running,当前线程不会看到它,因为它使用的是本地副本/ contextual to just。

将字段标记为volatile会修复它,因为当前Java内存模型下volatile的语义基本上确保所有volatile字段的读/写都返回到"主内存&# 34;,建立一个先发生过的关系。

那么为什么不将所有内容标记为volatile?或者就此而言,为什么一切都不是隐含的volatile而不是相反?答案是性能:易失性访问通常比非易失性访问慢很多,因此Java语言设计者决定只在您知道需要安全性时才使用它。 (同样适用于synchronized次访问)