线程中断总结
1、线程中断
每个线程都有一个与之相关联的 Boolean 属性,用于表示线程的中断状态(interrupted status)。中断状态初始时为 false;当另一个线程通过调用 Thread.interrupt()
中断一个线程时,会出现以下两种情况之一。如果那个线程在执行一个低级可中断阻塞方法,例如 Thread.sleep()
、 Thread.join()
或 Object.wait()
,那么它将取消阻塞并抛出InterruptedException
。否则, interrupt()
只是设置线程的中断状态。 在被中断线程中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情。中断状态可以通过 Thread.isInterrupted()
来读取,并且可以通过一个名为 Thread.interrupted()
的操作读取和清除。
中断是一种协作机制。当一个线程中断另一个线程时,被中断的线程不一定要立即停止正在做的事情。相反,中断是礼貌地请求另一个线程在它愿意并且方便的时候停止它正在做的事情。有些方法,例如 Thread.sleep()
,很认真地对待这样的请求,但每个方法不是一定要对中断作出响应。对于中断请求,不阻塞但是仍然要花较长时间执行的方法可以轮询中断状态,并在被中断的时候提前返回。 您可以随意忽略中断请求,但是这样做的话会影响响应。
中断的协作特性所带来的一个好处是,它为安全地构造可取消活动提供更大的灵活性。我们很少希望一个活动立即停止;如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态。中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止。
2、线程终止与中断
在早些时候通过stop方法强行停止线程,可能会导致线程异常结束、数据不一致的情况。但在一些情况下又需要安全的结束掉线程的操作如用户取消了线程在执行的任务,此时就可以通过线程中断通知线程,线程自己决定在何时停止运行。可以用 Java 平台提供的协作中断机制来构造灵活的取消策略。各活动可以自行决定它们是可取消的还是不可取消的,以及如何对中断作出响应,如果立即返回会危害应用程序完整性的话,它们还可以推迟中断。即使您想在代码中完全忽略中断,也应该确保在捕捉到 InterruptedException
但是没有重新抛出它的情况下,恢复中断状态,以免调用它的代码无法获知中断的发生。
并非所有的阻塞方法都抛出 InterruptedException。输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会提前返回。然而,对于套接字 I/O,如果一个线程关闭套接字,则那个套接字上的阻塞 I/O 操作将提前结束,并抛出一个 SocketException。java.nio 中的非阻塞 I/O 类也不支持可中断 I/O,但是同样可以通过关闭通道或者请求 Selector 上的唤醒来取消阻塞操作。类似地,尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的,但是 ReentrantLock 支持可中断的获取模式。
有些任务拒绝被中断,这使得它们是不可取消的。但是,即使是不可取消的任务也应该尝试保留中断状态,以防在不可取消的任务结束之后,调用栈上更高层的代码需要对中断进行处理。
语言规范中并没有为中断提供特定的语义,但是在较大的程序中,难于维护除取消外的任何中断语义。取决于是什么活动,用户可以通过一个 GUI 或通过网络机制,例如 JMX 或 Web 服务来请求取消。程序逻辑也可以请求取消。
3、线程中断API
1.interrupted()
public static boolean interrupted() { return currentThread().isInterrupted(true); }
从源码的注释中看出,它测试的是当前线程(current thread)的中断状态,且这个方法会清除中断状态。
2.isInterrupted()
public boolean isInterrupted() { return isInterrupted(false); }
从源码注释中可以看出,isInterrupted()方法不会清除中断状态。
3.interrupted()方法与 isInterrupted()方法的区别
从源代码可以看出,这两个方法都是调用的isInterrupted(boolean ClearInterrupted),只不过一个带的参数是true,另一个带的参数是false。
private native boolean isInterrupted(boolean ClearInterrupted);
因此,第一个区别就是,一个会清除中断标识位,另一个不会清除中断标识位。
再分析源码,就可以看出第二个区别在return 语句上:
public static boolean interrupted() { return currentThread().isInterrupted(true); } /************************/ public boolean isInterrupted() { return isInterrupted(false); }
interrupted()测试的是当前的线程的中断状态。而isInterrupted()测试的是调用该方法的对象所表示的线程。一个是静态方法(它测试的是当前线程的中断状态),一个是实例方法(它测试的是实例对象所表示的线程的中断状态)。
4、线程状态与中断
线程一共6种状态,分别是NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED(Thread类中有一个State枚举类型列举了线程的所有状态)。
线程的new状态表示还未调用start方法,还未真正启动。线程的terminated状态表示线程已经运行终止。这两个状态下调用中断方法来中断线程的时候,Java认为毫无意义,所以并不会设置线程的中断标识位,什么事也不会发生。
如果线程处于运行状态,那么该线程的状态就是RUNNABLE,但是不一定所有处于RUNNABLE状态的线程都能获得CPU运行,在某个时间段,只能由一个线程占用CPU,那么其余的线程虽然状态是RUNNABLE,但是都没有处于运行状态。而我们处于RUNNABLE状态的线程在遭遇中断操作的时候只会设置该线程的中断标志位,并不会让线程实际中断,想要发现本线程已经被要求中断了则需要用程序去判断。
当线程处于BLOCKED状态说明该线程由于竞争某个对象的锁失败而被挂在了该对象的阻塞队列上了。那么此时发起中断操作不会对该线程产生任何影响,依然只是设置中断标志位。
WAITING/TIMED_WAITING这两种状态本质上是同一种状态,只不过TIMED_WAITING在等待一段时间后会自动释放自己,而WAITING则是无限期等待,需要其他线程调用notify方法释放自己。但是他们都是线程在运行的过程中由于缺少某些条件而被挂起在某个对象的等待队列上。当这些线程遇到中断操作的时候,会抛出一个InterruptedException异常,并清空中断标志位。
总结NEW和TERMINATED对于中断操作几乎是屏蔽的,RUNNABLE和BLOCKED类似,对于中断操作只是设置中断标志位并没有强制终止线程,对于线程的终止权利依然在程序手中。WAITING/TIMED_WAITING状态下的线程对于中断操作是敏感的,他们会抛出异常并清空中断标志位。