这是我的游戏循环线程中的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,以确保它不会覆盖任何内容。
答案 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
次访问)