You can't forcefully stop a thread in Java.
Yes, there are methods like Thread.stop()
and related, but they've been deprecated for years for good reason.
Why is Thread.stop
deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath
exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath
kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
Because of the above, you shouldn't use those methods, nor rely on them working (many APIs with thread-heavy methods will happily ignore any calls to stop()
and interrupt()
).
Once we got that out of the way, you can still implement logic for your threads to terminate ASAP when you ask them to, in an elegant manner.
You need to do two things:
1.- Check for Thread.interrupted()
inside your run()
method. Something like this:
@Override
public synchronized void run() {
while (yourFinishCondition && !Thread.interrupted()) {
// do stuff until you finish, or until the thread is interrupted from the outside
}
}
2.- Invoke interrupt()
on every thread from your main method to signal them for termination when you need to, like this:
Thread.UncaughtExceptionHandler h = (thread, exception) -> {
thread0.interrupt();
thread1.interrupt();
thread2.interrupt();
};
A little PoC:
public class Main {
static class MyThread extends Thread {
public MyThread(String s) {
super(s);
}
@Override
public synchronized void run() {
while(!Thread.interrupted()) {
if (new Random().nextInt(1000000) == 7) {
throw new RuntimeException(Thread.currentThread().getName()+" oops!");
}
}
System.out.println(Thread.currentThread().getName()+" interrupted");
}
}
public static void main(String[] args) {
final MyThread thread0 = new MyThread("thread0");
final MyThread thread1 = new MyThread("thread1");
final MyThread thread2 = new MyThread("thread2");
Thread.UncaughtExceptionHandler h = (thread, exception) -> {
System.out.println(exception.getMessage());
thread0.interrupt();
thread1.interrupt();
thread2.interrupt();
};
thread0.setUncaughtExceptionHandler(h);
thread1.setUncaughtExceptionHandler(h);
thread2.setUncaughtExceptionHandler(h);
thread0.start();
thread1.start();
thread2.start();
}
}
Output:
thread2 oops!
thread1 interrupted
thread0 interrupted
Further reading: https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop()+to+terminate+threads