如何停止一个正在运行的java线程

对于线程来说,执行完特定任务后,自然的结束,当然是当好的选择。
但实际有很多情况,在任务没有执行完时,要求停止或者退出。
安全的停止一个正在运行的线程,也不是一件容易的事。

错误的关闭方法

已废弃的Thread.stop()

原因:

该方法天生是不安全的。使用thread.stop()停止一个线程,导致释放(解锁)所有该线程已经锁定的监视器(因沿堆栈向上传播的未检查异常ThreadDeath而解锁)。如果之前受这些监视器保护的任何对象处于不一致状态,则不一致状态的对象(受损对象)将对其他线程可见,这可能导致任意的行为。

无法直接停止的interrupt

Thread.interrupt()
如代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void example() throws Exception {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("enter running...");
int i=0;
while(i++ <Integer.MAX_VALUE -1 )
{
System.out.println(i);
}
System.out.println("end of run");
}
});
th.start();
System.out.println("main thread sleep");
Thread.sleep(1000);
System.out.println("main thread call interrupt");
th.interrupt();
System.out.println("over");
}
public static void main(String[] args) throws Exception {
example();
}

代码中会执行完循环再停止。

虽然调用interrupt方法,但线程中没有处理。

能正常工作的方法

普通循环业务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void example2() throws Exception {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("enter running...");
int i=0;
while(i++ <Integer.MAX_VALUE -1 && !Thread.interrupted())
{
System.out.println(i);
}
System.out.println("end of run");
}
});
th.start();
System.out.println("main thread sleep");
Thread.sleep(1000);
System.out.println("main thread call interrupt");
th.interrupt();
System.out.println("over");
}
public static void main(String[] args) throws Exception {
example2();
}

这里在业务逻辑中判断有没有打断。没有,就继续,有就停下来。

sleep和wait等情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void example3() throws Exception {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("enter running...");
try {
Thread.sleep(5*10*1000);
// synchronized (this) {
// wait();
// }
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end of run");
}
});
th.start();
System.out.println("main thread sleep");
Thread.sleep(1000);
System.out.println("main thread call interrupt");
th.interrupt();
System.out.println("over");
}
public static void main(String[] args) throws Exception {
example3();
}

原因:

  • 如果该线程正阻塞于Object类的wait()、wait(long)、wait(long, int)方法,或者Thread类的join()、join(long)、join(long, int)、sleep(long)、sleep(long, int)方法,则该线程的中断状态将被清除,并收到一个java.lang.InterruptedException。
  • 如果该线程正阻塞于interruptible channel上的I/O操作,则该通道将被关闭,同时该线程的中断状态被设置,并收到一个java.

等待IO时的情况

参考这里 如何中断ServerSocket accept()方法?
其于属于抛异常,进行处理。

比较好的方法-条件变量

1
2
3
4
5
6
7
8
9
10
11
12
public class BestPractice extends Thread {
private volatile boolean finished = false; // ① volatile条件变量
public void doStop() {
finished = true;
}
@Override
public void run() {
while (!finished) {
// do dirty work
}
}
}

这是最开始写多线程中推荐的写法。

总结

Java没有提供任何机制来安全地终止线程。但它提供了中断(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的的工作。
中断只是一种协作机制,需要被中断的线程自己处理中断。停止一个线程最佳实践是 中断 + 条件变量。

参考


如何停止一个正在运行的java线程
https://blog.fengcl.com/2017/08/22/how-to-stop-a-java-thread/
作者
frank
发布于
2017年8月22日
许可协议