在Java中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出。
Thread类定义了如下方法:
public boolean isInterrupted();//测试此线程是否已被中断。此方法不影响线程的中断状态 public void interrupt();//中断线程 public static boolean interrupted();//测试此线程是否已被中断,并清空中断标志位
interrupt()对线程的影响与线程的状态和在进行的IO操作有关,我们先考虑线程的状态:
- RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度
- WAITING/TIMED_WAITING:线程在等待某个条件或超时
- BLOCKED:线程在等待锁,试图进入同步块
- NEW / TERMINATED:线程还未启动或已结束
RUNNABLE
如果线程在运行中,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,没有任何其它作用。线程应该在运行过程中合适的位置检查中断标志位,如:
public class InterruptRunnableDemo extends Thread { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { // ... 单次循环代码 } System.out.println("done "); } public static void main(String[] args) throws InterruptedException { Thread thread = new InterruptRunnableDemo(); thread.start(); Thread.sleep(1000); thread.interrupt(); } }
WAITING/TIMED_WAITING
线程执行如下方法会进入WAITING状态:
public final void join() throws InterruptedException public final void wait() throws InterruptedException
执行如下方法会进入TIMED_WAITING状态:
public final native void wait(long timeout) throws InterruptedException; public static native void sleep(long millis) throws InterruptedException; public final synchronized void join(long millis) throws InterruptedException
在这些状态时,对线程对象调用interrupt()会使得该线程抛出InterruptedException,需要注意的是,抛出异常后,中断标志位会被清空,而不是被设置。比如:
public static void main(String[] args) throws InterruptedException { Thread t = new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println(isInterrupted()); } } }; t.start(); try { Thread.sleep(100); } catch (InterruptedException e) { } t.interrupt(); }
输出为:
false
捕获到InterruptedException,通常表示希望结束该线程,线程大概有两种处理方式:
-
- 向上传递该异常,这使得该方法也变成了一个可中断的方法,需要调用者进行处理。
- 有些情况,不能向上传递异常,比如Thread的run方法,它的声明是固定的,不能抛出任何受检异常,这时,应该捕获异常,进行合适的清理操作,清理后,一般应该调用Thread的interrupt方法设置中断标志位,使得其他代码有办法知道它发生了中断。
BLOCKED
如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正"中断"。
使用synchronized关键字获取锁的过程中不响应中断请求,这是synchronized的局限性。如果这对程序是一个问题,应该使用显式锁。
总结
interrupt方法不一定会真正"中断"线程,它只是一种协作机制,如果不明白线程在做什么,不应该贸然的调用线程的interrupt方法,以为这样就能取消线程。
对于以线程提供服务的程序模块而言,它应该封装取消/关闭操作,提供单独的取消/关闭方法给调用者,外部调用者应该调用这些方法而不是直接调用interrupt。