Java 等待方法完成,然后与System.println进行奇怪的交互
我正试图编写一个基因程序来完成一个游戏,但我遇到了一点障碍。当我调用此代码时:Java 等待方法完成,然后与System.println进行奇怪的交互,java,wait,Java,Wait,我正试图编写一个基因程序来完成一个游戏,但我遇到了一点障碍。当我调用此代码时: 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(
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);
}
}
显然,你是在一个单独的线程中运行你的游戏。假设线程被调用为
foo
,调用foo.join()
将阻止调用线程,直到foo
完成执行。只需将整个循环替换为foo.join()
。
如果从循环中删除
System.out.println()
调用,我相信编译器根本不在Java字节码中包含循环,认为它是多余的。显然,您是在一个单独的线程中运行游戏。假设线程被调用为foo
,调用foo.join()
将阻止调用线程,直到foo
完成执行。只需将整个循环替换为foo.join()
。
如果从循环中删除
System.out.println()
调用,我认为编译器根本不在Java字节码中包含循环,认为它是多余的。首先,这是一种非常糟糕的方法。这种方法称为“忙等待”,效率很低
问题很可能是对g.isDone
的读写未正确同步。因此,无法保证“等待”线程会看到将其设置为true的g.isDone
更新
有多种方法可以确保看到更新。最简单的方法是将isDone
声明为volatile
。另一种方法是在原语锁中执行读写操作
println()
调用“fixes”的原因是println在幕后进行了一些同步,这会导致意外的缓存刷新(或其他事情),从而使更新可见。(换句话说:你很幸运,但你是如何幸运的很难确定。)
更好的解决方案是使用另一种机制来协调两个线程
- 您可以使用
使一个线程等待另一个线程(完全!)终止Thread.join()
- 您可以使用
或锁存器
或类似工具来实现等待信号灯
- 您可以使用交付
未来的
执行器
,然后调用
,等待该执行器交付结果Future.get()
- 您甚至可以使用
和Object.wait
。。。虽然这是低级的,而且很容易出错Object.notify
另一个答案是: 如果从循环中删除System.out.println()调用,我相信编译器不会在Java字节码中包含该循环,认为它是多余的 如上所述,真正的问题是同步不足。从技术上讲,在一个线程中写入
isDone
和在另一个线程中读取isDone
之间需要有一种先发生后发生的关系。各种各样的东西都会让你。。。但如果没有这一点,编译器有权假设:
while (!g.isDone) {
// do nothing
}
到
我们不知道这是否真的发生了,或者更新到
isDone
的“不可见性”的实际原因是否是其他原因。(事实上,它可能是JVM版本/平台特定的。当然,您需要让JIT编译器转储方法的本机代码,并非常仔细地分析代码。)首先,这是一种非常糟糕的方法。这种方法称为“忙等待”,效率很低
问题很可能是对g.isDone
的读写未正确同步。因此,无法保证“等待”线程会看到将其设置为true的g.isDone
更新
有多种方法可以确保看到更新。最简单的方法是将isDone
声明为volatile
。另一种方法是在原语锁中执行读写操作
println()
调用“fixes”的原因是println在幕后进行了一些同步,这会导致意外的缓存刷新(或其他事情),从而使更新可见。(换句话说:你很幸运,但你是如何幸运的很难确定。)
更好的解决方案是使用另一种机制来协调两个线程
- 您可以使用
使一个线程等待另一个线程(完全!)终止Thread.join()
- 您可以使用
或锁存器
或类似工具来实现等待信号灯
- 您可以使用交付
未来的
执行器
,然后调用
,等待该执行器交付结果Future.get()
- 您甚至可以使用
Object.wait
if (!g.isDone) { // do nothing }