阻塞方法与中断方法

98 阅读3分钟

阻塞方法与中断方法

线程可能会阻塞或暂停执行,原因有多种:等待IO完成,等待获取一个锁,等待从sleep方法中醒来,或是等待另一个线程的计算结果。当线程阻塞时,它通常被挂起,并处于某种阻塞状态(BLOCKED,WAITING或TIME_WAITING)

线程的阻塞发生在阻塞方法中

阻塞方法(也就是会等待其他不可控事件的发生的方法)会抛出Interrupted-Exception。Java没法通过强制手段要求我们这样做,但如BlockingQuequeputtakeThread.sleep等方法会抛出这个受检查异常。

我们需要让会阻塞的方法抛出这个异常,因为他们确实可能出现意料之外的长时间阻塞。

通知线程中断

想象这样一个场景,线程a在等待线程b的计算结果,但是线程b的计算结果遥遥无期。

在早期的 Java 版本(Java 1.0 和 1.1)中,存在一种暴力终止线程的方法:Thread.stop()。这样暴力的代码当然可能会带来一些系统稳定性或内存安全问题,于是很快被废弃掉了。

作为代替,我们可以在任意一个线程中调用线程b的Thread.interrupt()方法。不同于Thread.stop()Thread.interrupt()会友好的通知该线程,该停止了。至于通知后这个线程怎么做,就不归这个方法管了。

如何接到通知?

调用Thread.interrupt()后,它会设置该线程的中断状态。设置后会有两个关键点:

  1. 可以通过调用该线程的 Thread.isInterrupted() 方法检查是否有中断请求。

  2. 如果该线程正在调用阻塞方法,那么会立即抛出一个InterruptedException同时会清除中断状态

  3. 如果该线程正在调用非阻塞方法,那么他暂时不会抛出InterruptedException,毕竟这个方法没有声明会抛出这玩意。

    1. 如果该线程随后调用了阻塞方法,这个阻塞方法会立即抛出InterruptedException同时会清除中断状态
    2. 如果该线程随后没有阻塞方法,那么线程可能无法检测到中断请求。

这个InterruptedException实际上就是来自别的线程的中断通知。不难发现,如果我们不编写处理InterruptedException的代码,那么线程在收到通知后什么也不会做。

如何处理中断请求?

  1. 传递InterruptedException。避开这个异常通常是最明智的策略——只需要将这个异常传递给它的调用者。传递这个异常的方法包括,根本不捕获这个异常(声明throws这个异常),或者捕获这个异常,然后执行简单的清理工作后再次抛出它。
  2. 恢复中断。有时候不能抛出InterruptedException,比如代码是Runnable的一部分。此时我们不得不正式处理这个中断请求,捕获它,然后通过调用线程上的interrupt方法。这样调用栈中更高层的代码将看到引发了一个中断。
  3. 别的方法不再赘述。上述两种已经足够应付大多数情况,