如何停止一个java线程

日常java编程中,多线程已经司空见惯,各种Thread,Runnable横飞。那应该如何正确的停止一个线程呢?
最原始的Thread类提供了一系列控制线程生命周期的方法——start(),stop(),stop(Throwable),suspend(),destroy()和resume()。
然而,现实是残酷的,现在,除了start()方法,别的都已经处于depreciated状态了
sun公司的文章Why Are Thread.stop, Thread.suspend,
Thread.resume and Runtime.runFinalizersOnExit Deprecated?
进行了一定的解释,当然了,现在destroy()方法也depreciated。

为什么不用stop()?

原文如下:

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.

原文非常官方且绕口,简单来说,就是调用stop()会释放线程的所有锁,同时抛出一个ThreadDeath error。这个时候,锁住的变量因为突然被释放就可能导致了数据不一致,会造成其他使用这个变量的线程出现错误。
而且让正在被修改的数据被其他线程可见,也违反了java的沙箱模型。

为什么不用suspend()和resume()?

因为死锁
suspend()方法就是将一个线程挂起(暂停),resume()方法就是将一个挂起线程复活继续执行。
但是suspend()方法调用的时候不会释放锁,这样,如果Thread1调用了suspend(),同时hold住了一个变量,而Thread2需要使用这个变量之后才能让Thread1去resume(),死锁就形成了。

终止线程的推荐方法

终止一个线程的最佳方法就是让其退出它的run()方法,常见的就是使用一个volatile的终止标志:

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
public class BestPractice extends Thread {
private volatile boolean finished = false; // ① volatile条件变量

public void stopThread() {
finished = true; // ② 发出停止信号
}

@Override
public void run() {
while (!finished) { // ③ 检测条件变量
// do dirty work // ④业务代码
}
}

public static void main(String args[]) throws InterruptedException
{

BestPractice task = new BestPractice();
task.start();
...

//stop in this manner
task.stopThread();
t.interrupt();
}
}

这里面,有几个注意点:

  • 使用violate boolean变量来标识线程是否停止。
  • 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性。
  • 对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO。

参考

How to Stop a Thread or a Task