java: stop thread on ESC
I am writing a simple game in Java, and I have a following issue:
I have a controlling class, called MainGameFrame
, in which there is initialized gameThread
. MainGameFrame
has a key listener for Esc key, so that it pauses/resumes gameThread
. However, this doesn't work:
public void keyPressed(KeyEvent e) {
// pause the game
synchronized(gameThread) {
if(e.getKeyCode() == e.VK_ESCAPE) {
try {
if(gameThread.getState() == Thread.State.WAITING) {
System.out.println("continue");
gameThread.notify();
System.out.println("after continue");
} 开发者_如何转开发else {
System.out.println("pause");
gameThread.wait();
System.out.println("after pause");
}
} catch (InterruptedException ex) {
Logger.getLogger(MainGameFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
It will pause on Esc and output "pause", but not "after pause".
gameThread.wait()
doesn't make the gameThread thread to pause. It makes the current thread (i.e. the event dispatch thread) to wait. Since the EDT is waiting, it can't receive a keypressed event anymore: the entire GUI freezes
Read the Java tutorial on concurrency, and particularly the following page: http://download.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
You should probably change the value of an AtomicBoolean variable, which is regularly inspected by the game thread to know if it has to pause. The game thread would then wait on a shared lock object. When resuming, change the boolean variable again and notify the game thread.
It waits until another thread calls notify(). Your not doing that, so "after pause" is never printed.
What do you want to achieve with gameThread.wait()?
You have the idea of wait and notify backwards.
One thread can not directly pause, or kill another thread. You can, however, change a variable that is shared between threads. When the interested thread sees that the variable has changed it may "pause" itself.
private static volatile boolean paused = false;
private static ReentrantLock pauseLock = new ReentrantLock();
private static Condition unpaused = pauseLock.newCondition();
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
if (!paused)
paused = true;
else {
pauseLock.lock();
try {
paused = false;
unpaused.signal();
} finally {
pauseLock.unlock();
}
}
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(200,200));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
Thread gameThread = new Thread() {
public void run() {
while (true) {
System.out.println("running");
// presumably the game rendering loop
if (paused) {
pauseLock.lock();
try {
try {
unpaused.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
pauseLock.unlock();
}
}
}
}
};
gameThread.start();
}
I would also avoid using wait and notify directly as there are some caveats you must be aware of when using wait & notify (such as your thread that is waiting actually has to wait inside a loop because it can be woken up without notify ever being called). http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4308396
You should also be sure to always acquire and release locks in the following pattern, if not using synchronized blocks:
l.lock();
try {
// code
} finally {
l.unlock();
}
http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Lock.html
I'd also recommend reading: http://www.javaconcurrencyinpractice.com/ There are more pitfalls than you would think when doing concurrent programming in Java.
The reason that one can not pause or kill a thread from another thread is that the thread doing the pausing or killing has no idea where in execution the other thread is and what resources the other thread might hold.
What would happen if Thread A paused Thread B and Thread B happened to hold several resources that Thread A will need later? What happens if Thread A kills Thread B while Thread B is using a resource that it is supposed to do some special clean up on?
精彩评论