Since you have access to those threads (I mean you can modify their code) then you can:
- Do not allow the user to stop the application from exiting in a normal way. You are going to stop it for him once you are prompted by him to do so. This is where you need the hook. For example if the application is based on a
JFrame
then add a windowClosing()
hook to gracefully stop the running Thread
s and at the same time set the default close operation of the frame to DO_NOTHING_ON_CLOSE. Take a look at my code in this answer below...
- Put a stoping flag in the infinite loops, making them finite.
- When the shutdown is going to occur, alter those flags so that each
Thread
exits the loop.
- When out of the loop the
Thread
will make any changes necessary (with full access to all of its variables) and then exit the run()
method.
- Meanwhile, in the main
Thread
(i.e. the Thread
which marked all other Thread
s to stop), you are going to join()
each finite Thread
.
This method allows to wait for a Thread
to execute completely (i.e. exit its run()
method).
- Finally, the main
Thread
, is going to call for example System.exit(0);
to exit the whole application, because this is supposedly the last desired action by the user.
Note: this assumes that the user is going to close the application with normal actions. If for example the user is going to kill the application via the Windows' Task Manager for example, then this approach won't work. I don't know about shutdown hooks on Thread
s...
Follows example code (read the comments):
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GraceDown {
private static class MyThread extends Thread {
private boolean keepGoing = true; //This is the stopping flag.
private final int i; //In this case i marks the 'id' of the Thread, but it is also an arbitary variable that the Thread has acccess in its final moments...
public MyThread(final int i) {
this.i = i;
}
private synchronized boolean isKeepGoing() {
return keepGoing; //Tells if we should exit the loop and start the stopping operation...
}
public synchronized void startShutdown() {
keepGoing = false; //Tells we should exit the loop and start the stopping operation.
}
@Override
public void run() {
while (isKeepGoing()) { //After every complete loop, we check the if we can go on.
//Your 'infinite' loop actions go in here:
try { Thread.sleep(1000); } catch (final InterruptedException ie) {}
System.out.println("Thread " + i + " running...");
}
//Your gracefull shutdown actions go here...
System.out.println("Thread " + i + ": my stopping actions go here! Look, I have access to all my variables (such as i)! ;)");
}
}
public static void main(final String[] args) {
//Create and start the Threads:
final MyThread[] myThreads = new MyThread[5];
for (int i = 0; i < myThreads.length; ++i)
myThreads[i] = new MyThread(i);
for (int i = 0; i < myThreads.length; ++i)
myThreads[i].start();
final JFrame frame = new JFrame("Lets close me...");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); //IMPORTANT STEP! This means we are going to close the application instead of it being closed automatically when the user presses to close the window...
//Adding the 'hook':
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(final WindowEvent wevt) {
System.out.println("Initializing shutdown...");
//This is where the thread is going to stop all others...
for (int i = 0; i < myThreads.length; ++i)
myThreads[i].startShutdown();
//Wait for all threads to stop:
for (int i = 0; i < myThreads.length; ++i)
try { myThreads[i].join(); } catch (final InterruptedException ie) { System.err.println("Error gracefully shutdowning the Thread!!!"); }
//Now we can exit the JVM:
System.out.println("Exiting JVM...");
System.exit(0); //Code '0' indicates exiting without error(s).
}
});
frame.getContentPane().add(new JLabel("Close this window and the Threads will shutdown gracefully...", JLabel.CENTER));
frame.pack();
frame.setLocationRelativeTo(null); //Put the packed frame on the center of the default screen.
frame.setVisible(true);
}
}
The above should print something like this:
Thread 1 running...
Thread 4 running...
Thread 0 running...
Thread 2 running...
Thread 3 running...
Thread 3 running...
Thread 4 running...
Thread 2 running...
Thread 1 running...
Thread 0 running...
Initializing shutdown...
Thread 2 running...
Thread 2: my stopping actions go here! Look, I have access to all my variables (such as i)! ;)
Thread 0 running...
Thread 0: my stopping actions go here! Look, I have access to all my variables (such as i)! ;)
Thread 1 running...
Thread 4 running...
Thread 3 running...
Thread 3: my stopping actions go here! Look, I have access to all my variables (such as i)! ;)
Thread 4: my stopping actions go here! Look, I have access to all my variables (such as i)! ;)
Thread 1: my stopping actions go here! Look, I have access to all my variables (such as i)! ;)
Exiting JVM...
This is a minimal solution I can see in your problem as you described it.