在Java中,你不能强制停止一个线程。
是的,有像 Thread.stop()
等方法,但它们已经被废弃多年了 ,有充分的理由。
为什么 Thread.stop 被废弃?
因为它本质上是不安全的。停止一个线程会导致它解锁其所有已锁定的监视器(当 ThreadDeath 异常向上传播时,监视器被解锁)。如果其中任何受这些监视器保护的对象处于不一致状态,则其他线程现在可能以不一致的状态查看这些对象。这样的对象被称为损坏的。当线程操作损坏的对象时,可能会导致任意行为。这种行为可能很微妙,很难检测,也可能非常明显。与其他未经检查的异常不同,ThreadDeath 会在默默地终止线程;因此,用户没有警告指出程序可能已损坏。损坏可以在实际损坏发生后的任何时间表现出来,甚至在将来几个小时或几天内。
基于上述原因,您不应使用这些方法,也不应依赖它们正常工作(许多具有重负线程方法的API将快乐地忽略对 stop()
和 interrupt()
的任何调用)。
一旦我们排除了以上的方法,你仍然可以通过优雅的方式实现逻辑,让你的线程尽快终止。
你需要做两件事:
1. - 在你的 run()
方法中检查 Thread.interrupted()
。像这样:
@Override
public synchronized void run() {
while (yourFinishCondition && !Thread.interrupted()) {
}
}
2. 当需要结束线程时,在主方法中通过调用interrupt()
来向所有线程发出终止信号,例如:
Thread.UncaughtExceptionHandler h = (thread, exception) -> {
thread0.interrupt();
thread1.interrupt();
thread2.interrupt();
};
一点小的概念验证(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();
}
}
输出:
thread2 oops!
thread1 interrupted
thread0 interrupted
进一步阅读:
https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop()+to+terminate+threads。请勿使用Thread.stop()来终止线程。