1.中断标记

对于有些比较耗时的任务,我们往往会采用专门的工作者线程来负责其执行,如果中途要取消这类任务的执行,那么我们就需要借助 Java 线程中断机制。Java平台会为每个线程维护一个被称为中断标记的布尔型状态变量用于表示相应线程是否接收到了中断,中断标记值为 true 表示相应线程收到了中断。 目标线程可以Thread.currentThread().islnterrupted()调用来获取该线程的中断标记值,也可以通过Thread.interrupted()来获取并重置(也称清空)中断标记值,即 Thread.interrupted()会返回当前线程的中断标记值并将当前线程中断标记重置为 false。调用一个线程的interrupt()相当于将该线程(目标线程)的中断标记置为 true。

2.中断响应

目标线程检查中断标记后所执行的操作,被称为目标线程对中断的响应,简称中断响应。设有个发起线程 originator 和目标线程 target, 那么 target 对中断的响应一般包括:

  • 无影响。originator 调用 target.interrupt()不会对 target 的运行产生任何影响。这种情形也可以称为目标线程无法对中断进行响应。
  • 取消任务的运行。originator调用target. interrupt()会使target在侦测到中断(即中断标记值为true) 那一刻所执行的任务被取消(中止),而这并不会影响target继续处理其他任务。
  • 工作者线程停止。originator调用target.interrupt()会使target终止,即target的生命周期状态变更为TERMINATED 。

3.Interrupted Exception异常

Java 标准库中的许多阻塞方法对中断的响应方式都是抛出 InterruptedException 等异常,能够响应中断的方法通常是在执行阻塞操作前判断中断标志,若中断标志值为 true则抛出InterruptedException。依照惯例,凡是抛出 InterruptedException 异常的方法,通常会在其抛出该异常之前将当前线程的线程中断标记重置为 false。
如果发起线程给目标线程发送中断的那一刻,目标线程已经由于执行了一些阻塞方法/操作而被暂停(生命周期状态为 WAITING 或者 BLOCKED) 了,那么此时 Java 虚拟机可能会设置目标线程的线程中断标记并将该线程唤醒,从而使目标线程被唤醒后继续执行的代码再次得到响应中断的机会。
因此,Java 应用层代码通常可以通过对 InterruptedException 等异常进行处理的方式来实现中断响应。对 InterruptedException异常的正确处理方式包括以下几种。

  • 不捕获 lnterruptedException。如果应用代码的某个方法调用了能够对中断进行响应的阻塞方法,那么我们也可以选择在这个方法的异常声明 (throws) 中也加个InterruptedException。这种做法实质上是当前方法不知道如何处理中断比较恰当,因此将“难题“抛给其上层代码(比如这个方法的调用方)。
  • 捕获InterruptedException后重新将该异常抛出。使用这种策略通常是由于应用代码需要捕获 InterruptedException 并对此做一些中间处理(比如处理部分完成的任务),接着再将“难题“抛给其上层代码 。
  • 捕获 lnterruptedException 并在捕获该异常后中断当前线程。这种策略实际上在捕获到 InterruptedException 后又恢复中断标志,这相当于当前代码告诉其他代码:“我发现了中断,但我并不知道如何处理比较妥当,因此我为你保留了中断标记,你看着办吧!“。