java: stop thread on ESC - java

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?

Related

How do I only delay a specific method?

I am still a very early coder and still don't know everything about java so my code is still a bit messy, sorry about that. I am making a simple platformer game with the graphics g class and I'm trying to figure out how to delay a method without pausing the entire script.
I have tried Thread.sleep() and TimeUnit.SECONDS.sleep() but both of these freeze other methods running at the time, and the Timer that keeps the game running.
private static Timer timer;
private int delay = 10;
public Gameplay() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(delay, this);
timer.start();
}
public void moveDown() {
if (play == true) {
Playsound("drop");
dropping = true;
//pause here for 1 second without freezing timer or other running methods
dropping = false;
}
}
I want the program to continue running while waiting, but the things I have tried always freeze the entire program
One trick from my side
public void moveDown() {
if (play == true) {
Playsound("drop");
dropping = true;
//create a thread which will execute your method and set sleep on that thread
dropping = false;
}
try this
...
Thread thread = new Thread(() -> moveDown());
thread.run();
...
void moveDown() {
//do some work
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//continue your work
}
explanation
you need to stop thread , but you dont want to stop current thread :)
that means you need to run your work on background thred and stop only it,
this is not best solution , its just for explanation how you can do it.

How to pause and resume game using the escape key?

How do I make my Escape key pause and resume a game when it is pressed twice? I have tried calling the key adapter class in my thread class but it only pauses the game; it does not resume it.
Here is the code that pauses the game:
//the thread class
class recMove extends Thread {
JFrame b;
public boolean running=true;
//public boolean gameover=false;
public recMove(JFrame b){
this.b=b;
pauseGame();
}
public void run(){
while(running){
b.repaint();
try {
Thread.sleep(100);
} catch(InterruptedException e){}
}
}
public void pauseGame(){
addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e) {
int keyCode=e.getKeyCode();
if(keyCode==KeyEvent.VK_ESCAPE) {
running=false;
System.out.println("escape pressed");
}
if(keyCode==KeyEvent.VK_END){
System.exit(0);
}
}
});
}
}
It doesn't resume because the thread is killed, when you press escape the running value is set to false therefore this loop:
while(running){
b.repaint();
try {
Thread.sleep(100);
} catch(InterruptedException e){}
}
Will end, which in turn will make the run() method exit. When a run() method of a class extending Thread (or implementing Runnable) exits that thread is being killed so there's nothing more listening for your keypresses.
You need to change your run() logic so it doesn't exit when running is set to false but instead wait for the next keypress or add the listener somewhere else (in a different thread) so it will create a new thread with the game again.
Furthermore in your logic esc only changes running to false, if you want it to then resume the game you should check the state of running and if it's false you should set it to true instead.

How to destroy a thread?

I'm making simple game, here is the code:
public class Game extends Canvas implements Runnable {
public void start() {
t = new Thread(this);
t.start();
}
#Override
public void run() {
setVisible(true); // visibility of the thread turn on
while (!t.isInterrupted()) {
if(condition for end the game) {
t.interrupt(); //here i need to destroy the thread
setVisible(false); //visibility to off
}
update();
render();
try {
Thread.sleep(20);
} catch(InterruptedException e) {}
}
}
}
I have another class which extends JFrame and this class is introducing main menu, if my "condition for end the game" is true, the thread dissapears and the menu is visible again, its good, but if i want to start new game again, the behavior of the thread is strange- its seems like the Thread.sleep() method changed from 20 to 10 because all its going faster, probably i need to kill the thread, but i dont know how, thanks
Simple, break the loop:
if(condition for end the game) {
t.interrupt(); //here i need to destroy the thread
setVisible(false); //visibility to off
break;
}
You end the loop and the thread will end.
The easiest way to terminate a Thread is to exit the run function. There is no special handling required, a simple return does the trick.
For you game you might want to consider using a ScheduledExecutorService, which allows you to schedule a Runnable to run at a fixed rate:
executor.scheduleAtFixedRate(gameLoop, 0, 1000/TARGET_FPS, TimeUnit.MILLISECONDS);
Keep in mind that you then need to take out the actual looping of your gameLoop, because that is done by the fixed-rate calling, which will reduce it to:
public void run() {
if (pause == false) {
update();
render();
}
}
Where pause is a boolean should you for some reason want to put the rendering on pause for a while.
With this setup you can terminate the game simply by calling executor.shutdown() which will then suppress any further calls to the runnable.
Not really on-topic, but I'm making a game too, and for pacing I'm using Timer (from swingx):
public class MainGameLoop implements ActionListener{
Timer timer;
public static void main(...){
timer = new Timer(10, this);
timer.start();
}
public void actionPerformed(ActionEvent e) {
...
}
}
Working well to me.

Multithreading issue can't wait for thread to finish

I am having problem with multithreading.
When I am tring to use wait() and notify() or join() I am getting InterruptedException.
I have 2 threads in a WHILE loop and I want to wait until both of them are finished.
this is my code:
while (!GamePanel.turn)
{
if(GamePanel.s.isRunning)
{
synchronized (GamePanel.s.thread)
{
try {
GamePanel.s.thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//Player selected random moves
if(GamePanel.selectedMove == 1)
{
//Get computer's Random move.
strategy.getRandomMove();
}
else
{
//Player selected AI moves
if(GamePanel.selectedMove == 2)
{
//Get computer's AI move.
strategy.getMove();
System.out.println(strategy.move);
}
}
//Perform the next move of the computer.
Rules.makeMove(GamePanel.dimples, strategy.move, false);
}
both strategy.getMove() and Rules.makeMove() are threads.
for each thread I've created my own start() and stop() methods :
public void start()
//This function starts the thread.
{
if (!isRunning)
{
isRunning = true;
thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
}
private void stop()
//This function stops the thread.
{
isRunning = false;
if (thread != null)
{
thread.interrupt();
}
thread = null;
}
I have also tried to do thread.stop() but still same problem.
My question is how can I make the WHILE loop to wait until both threads are done??
You might consider to switching your code to use a CountDownLatch. You would create the latch like the following and all 3 threads would share it:
final CountDownLatch latch = new CountDownLatch(2);
Then your two threads would decrement the counter as they finish:
countDown.countDown();
And your waiting thread would do:
countDown.await();
It would be awoken after both of the threads finish and the latch goes to 0.

How to Interrupt in Java

In Java, let's say I have a GUI with 2 buttons, Go and Pause.
When I press Go, "Hello" gets printed out over and over again. When I press Pause, "Hello" no longer gets printed to the screen.
Example: User presses Go button. "Hello" gets printed out for 1 minute until the user presses "Pause."
What is the proper way to express this approach in Java? Is it equivalent to my commented pseudocode within the goButton source?
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
// while user has not pressed the pause button
printHello();
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
Thanks
In order to get this to work, in reasonable fashion, you will need a Thread. This is executed in the background until such time as you decide to cancel/pause it.
This is an EXTREMELY basic example. Normally I'd wrap the task and the GUI up in appropriate classes rather then accessing static references, but it gives a basic idea
public class TestHello {
private static HelloTask task;
public static void main(String[] args) {
Thread thread = new Thread((task = new HelloTask()));
thread.setDaemon(true);
thread.start();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
JButton goButton = new JButton("Go");
JButton stopButton = new JButton("Stop");
goButton.setActionCommand("Go");
stopButton.setActionCommand("Stop");
ActionHandler handler = new ActionHandler();
goButton.addActionListener(handler);
stopButton.addActionListener(handler);
frame.add(goButton);
frame.add(stopButton);
frame.setVisible(true);
}
public static class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Go")) {
task.start();
} else if (e.getActionCommand().equals("Stop")) {
task.pause();
}
}
}
public static class HelloTask implements Runnable {
private static final Object WAIT_LOCK = new Object();
private boolean dump = false;
public void start() {
synchronized (WAIT_LOCK) {
dump = true;
WAIT_LOCK.notify();
}
}
public void pause() {
synchronized (WAIT_LOCK) {
dump = false;
WAIT_LOCK.notify();
}
}
#Override
public void run() {
while (true) {
while (dump) {
System.out.println("Hello");
}
try {
synchronized (WAIT_LOCK) {
WAIT_LOCK.wait();
}
} catch (Exception e) {
}
}
}
}
}
Some further read:
Java Concurrency
Concurrency in Swing
Caveats
NEVER try and modify the GUI from any thread other then the Event Dispatching Thread.
To have responsive UI you would usually have to run printHello() in separate thread. Then as you do processing in this thread, for example, after every print statement, you check some flag boolean isPaused; and stop execution if it is true. When pause button is clicked you set the value of this flag to true.
You need to implement your loop in a separate thread. Otherwise the GUI will become irresponsive and the user might not be able to click the Pause button at all.
With this threaded approach, you also need a flag which indicates whether or not to print out the message. The printing loop can simply stop executing the thread when the flag is set to no longer print.
what about htis:
boolean flag=true;
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
while(true)
{
printHello();
}
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
You can do this in a few ways the simplest being:
You have a boolean flag, keepPrinting and you set it to true when you push the Go button, false when you push the Pause. Next you have a thread somewhere executing a while loop which will print nothing when keepPrinting is false.
The threading here is really important, without it you're going to have your GUI freeze once the user pushes a button as the program prints hello and happily ignores anything else.
Pseudo Code
//GUI
public ThreadedPrinter greeter;
void ButtonGoPushed(args){
greeter.keepPrinting = true;
}
void ButtonPausePushed(args){
greeter.keepPrinting = false;
}
//ThreadedPrinter
boolean keepPrinting
void run(){
while(true){
if(keepPrinting){
print("Hello");
}
sleep(5); //Make sure that this thread yields if the system doesn't do it automatically
}
The good news about java concurrency versus say C++ is that this will just work, you don't have to worry about the boolean being crazy and inconsistent because in java variable sets are atomic. If you want to do more than just set the variable, make a synchronized method that sets the variable and does anything else you want.
Basically to keep UI responsive such task need to be performed in other thread.
There can be various ways in which you can implement this mechanism in java.
I have used simple mechanism of Runnalbe and volatile flag which ensure that thread exists when you call cancelPrint() method
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
//start the thread here
}
else if(e.getSource() == pauseButton)
{
//call cancel here
}
}
public class HelloPrinter implements Runnable {
volatile boolean cancel = false;
#Override
public void run() {
while (!cancel) {
printHello();
}
}
public void cancelPrint() {
cancel = true;
}
}
I assume you want to do more than just printouts. Take a look at Swing Worker.
It allows you to pretty easily write your GUI-related code that gets executed in the AWT Event Thread and your long-executing code in other thread(s) and pass values back and forth. This will help prevent any GUI lockup issues you might experience.

Categories

Resources