阻塞方法与中断方法
线程可能会阻塞或暂停执行,原因有多种:等待IO完成,等待获取一个锁,等待从sleep方法中醒来,或是等待另一个线程的计算结果。当线程阻塞时,它通常被挂起,并处于某种阻塞状态(BLOCKED,WAITING或TIME_WAITING)
线程的阻塞发生在阻塞方法中
阻塞方法(也就是会等待其他不可控事件的发生的方法)会抛出Interrupted-Exception。Java没法通过强制手段要求我们这样做,但如BlockingQueque的put和take和Thread.sleep等方法会抛出这个受检查异常。
我们需要让会阻塞的方法抛出这个异常,因为他们确实可能出现意料之外的长时间阻塞。
通知线程中断
想象这样一个场景,线程a在等待线程b的计算结果,但是线程b的计算结果遥遥无期。
在早期的 Java 版本(Java 1.0 和 1.1)中,存在一种暴力终止线程的方法:Thread.stop()。这样暴力的代码当然可能会带来一些系统稳定性或内存安全问题,于是很快被废弃掉了。
作为代替,我们可以在任意一个线程中调用线程b的Thread.interrupt()方法。不同于Thread.stop(),Thread.interrupt()会友好的通知该线程,该停止了。至于通知后这个线程怎么做,就不归这个方法管了。
如何接到通知?
调用Thread.interrupt()后,它会设置该线程的中断状态。设置后会有两个关键点:
-
可以通过调用该线程的
Thread.isInterrupted()方法检查是否有中断请求。 -
如果该线程正在调用阻塞方法,那么会立即抛出一个
InterruptedException,同时会清除中断状态。 -
如果该线程正在调用非阻塞方法,那么他暂时不会抛出
InterruptedException,毕竟这个方法没有声明会抛出这玩意。- 如果该线程随后调用了阻塞方法,这个阻塞方法会立即抛出
InterruptedException,同时会清除中断状态。 - 如果该线程随后没有阻塞方法,那么线程可能无法检测到中断请求。
- 如果该线程随后调用了阻塞方法,这个阻塞方法会立即抛出
这个
InterruptedException实际上就是来自别的线程的中断通知。不难发现,如果我们不编写处理InterruptedException的代码,那么线程在收到通知后什么也不会做。
如何处理中断请求?
- 传递
InterruptedException。避开这个异常通常是最明智的策略——只需要将这个异常传递给它的调用者。传递这个异常的方法包括,根本不捕获这个异常(声明throws这个异常),或者捕获这个异常,然后执行简单的清理工作后再次抛出它。 - 恢复中断。有时候不能抛出
InterruptedException,比如代码是Runnable的一部分。此时我们不得不正式处理这个中断请求,捕获它,然后通过调用线程上的interrupt方法。这样调用栈中更高层的代码将看到引发了一个中断。 - 别的方法不再赘述。上述两种已经足够应付大多数情况,