[英]Wait for method to Finish, and weird interaction with System.println
我正在尝试编写一个遗传程序来玩游戏,但是我遇到了一些麻烦。 当我调用此代码时:
public double playMap (GameBoard gb, Player p) {
gb.playerController = p;
Game g = new Game(gb);
int initHP = 0;
for (Unit u : gb.enemy.units) {
initHP += u.maxHP;
}
g.playGame(false);
int finalHP = 0;
for (Unit u : gb.enemy.units) {
finalHP += u.currHP;
}
System.out.println(" " + initHP);
System.out.println(" " + finalHP);
System.out.println(" " + (finalHP - initHP));
if (initHP == finalHP) {
return -10;
}
return initHP - finalHP;
}
g.playGame()行没有时间完成,我从函数中得到了不正确的结果。 我可以等一下游戏结束了
while (!g.isDone) {
System.out.println(g.isDone);
}
但不能使用相同的while循环而不使用print语句。 我知道必须有一个更优雅的解决方案,而且我似乎无法实现我所看到的方法。 另外,如果有人知道为什么我需要在while循环中使用print语句来等待它,那也很好。
提前致谢。
新增游戏:
public void playGame(boolean visual) {
Global.visual = visual;
if (Global.visual) {
JFrame application = new JFrame();
application.setBackground(Color.DARK_GRAY);
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(this);
application.setSize(500, 400); // window is 500 pixels wide, 400 high
application.setVisible(true);
}
PlayerInput pi = new PlayerInput();
this.addKeyListener(pi);
final Timer timer = new Timer(10/60, null);
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pi.addPressed();
if (update(pi)) {
// application.setVisible(false);
// application.dispose();
System.out.println(gb.toString());
isDone = true;
timer.stop();
}
pi.reset();
}
};
timer.addActionListener(listener);
timer.start();
while (!isDone) {
System.out.println(isDone);
}
}
首先,这是一种非常糟糕的方法。 这种方法称为“繁忙等待”,效率很低。
问题很可能是g.isDone
未正确同步。 因此,无法保证“等待”线程将永远看到g.isDone
的更新(将其设置为true)。
有多种方法可以确保可以看到更新。 最简单的方法是将isDone
声明为volatile
。 另一个是在原始锁内进行读取和写入。
println()
调用“修复”问题的原因是println在幕后进行了一些同步,这导致了偶然的缓存刷新(或类似的事情)使您的更新可见。 (换句话说:您很幸运,但是确切地讲您如何幸运很难被束缚。)
更好的解决方案是使用另一种机制来协调两个线程。
您可以使用Thread.join()
以便一个线程等待另一个线程终止(完全!)。
您可以使用Latch
或Semaphore
或类似方法来实现等待。
您可以使用提供Future
的Executor
,然后调用Future.get()
等待交付结果。
您甚至可以使用Object.wait
和Object.notify
...,尽管这是低级的并且容易出错。
如果没有看到完整的上下文,很难判断哪种方法最合适。 但是他们总比忙于等待更好。
另一个答案是这样的:
如果您从循环中删除System.out.println()调用,我相信编译器根本不会在Java字节码中包含该循环,因为它是多余的。
如上所述,真正的问题是同步不足。 要技术,需要有一个之前发生的写入的关系isDone
在一个线程和读isDone
在另一个。 各种各样的事情都会导致……但是没有那个,编译器有权假定:
例如,如果没有thebefore-before ,则允许编译器进行优化
while (!g.isDone) {
// do nothing
}
至
if (!g.isDone) {
// do nothing
}
我们不知道这种情况是否真的发生,还是不知道对isDone
更新的“不可见性”的真正原因是什么。 (实际上,它可能是特定于JVM版本/平台的。要确定,您将需要让JIT编译器为方法转储本机代码,并非常仔细地分析代码。)
显然,您是在单独的线程中运行游戏。 假设该线程名为foo
,则调用foo.join()
将阻塞调用线程,直到foo
完成执行为止。 您可以简单地将整个循环替换为foo.join()
。 如果您从循环中删除System.out.println()
调用,我相信编译器根本不会在Java字节码中包含该循环,因为它是多余的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.