If the thread doesn't need to do any cleanup on termination, then just make the thread a daemon thread:
thread.setDaemon(true)
Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads.
This method must be invoked before the thread is started.
Using setDaemon()
is by far the easiest thing to do and is what is recommended if your processing thread does not need to do any cleanup (e.g. it does not need to complete or roll-back an atomic commit transaction) before the application exits.
If you need to perform cleanup for the thread before it exits, then it is better to not make the thread a daemon, but instead make the thread interruptible, issue an interrupt and handle the interrupt.
For example, manage your thread using an ExecutorService
, that you shutdown in the Application stop()
method using a method similar to that mentioned in the ExecutorService javadoc:
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
Note that the shutdownNow() call implicitly sends interrupts to your thread, which will not be effective unless your thread processor is explicitly coded to handle the interrupt.
typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
If the cancellation really isn't working and you just want to give up, you can replace the System.err.println()
statement in the above code with a System.exit()
.
And your thread task logic also needs to deal with the interruption:
public class PrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted())
queue.put(p = p.nextProbablePrime());
} catch (InterruptedException consumed) {
/* Allow thread to exit */
}
}
public void cancel() { interrupt(); }
}
Also note, if you aren't subclassing Thread, but are instead implementing Runnable for library type code, then you don't want to swallow the interrupt as above, but instead you want to restore the interrupted status, similar to below (read "Dealing with InterruptedException" below to further understand why restoring the interrupted status is desirable for library code):
public class TaskRunner implements Runnable {
private BlockingQueue<Task> queue;
public TaskRunner(BlockingQueue<Task> queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
}
catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
}
See also some reference documents (from which some of the info in this answer was copy and pasted):