java并发编程()阻塞方法与中断方法

时间:2021-05-15 18:43:18

看完这篇,我感觉我对java多线程又懵逼了。

线程可能会阻塞或暂停执行,原因有多种:

  • 等待I/O操作结束
  • 等待获得一个锁
  • 等待从Thread.sleep方法中醒来
  • 等待另一个线程计算的结果

当线程阻塞时,它通常被挂起,并处于某种阻塞状态:

BLOCKED、WAITING或TIMED_WAITING

阻塞操作与执行时间很长的普通操作的差别在于:

被阻塞的线程必须等待某个不受它控制的事件发生后才能继续执行,例如等待I/O操作的完成,等待某个锁变成可用,或者等待外部计算的结束。

当某个外部事件发生时,线程被置回RUNNABLE状态,并可以再次被调度执行。

BlockingQueue的put和take等方法会抛出受检查异常(Checked Exception)InterruptedException,这与类库中其他一些方法的做法相同,例如Thread.sleep。当某方法抛出InterruptedException时,表示该方法是一个阻塞方法,如果这个方法被中断,那么它将努力提前结束阻塞状态。

Thread提供了interrupt方法,用于中断线程或者查询线程是否已经被中断。每个线程都有一个布尔类型的属性,表示线程的中断状态,当中断线程时将设置这个状态。

中断是一种协作机制。一个线程不能强制其他线程停止正在执行的操作而去执行其他的操作。当线程A中断B时,A仅仅是要求B在执行到某个可以暂停的地方停止正在执行的操作——前提是如果线程B愿意停止下来。虽然在API或者语言规范中并没有为中断定义任何特定应用级别的语义,但最常使用的中断情况就是取消某个操作。方法对中断请求的响应度越高,就越容易及时取消那些执行时间很长的操作。

当在代码中调用了一个将抛出InterruptedException异常的方法时,你自己的方法也就变成了一个阻塞方法,并且必须要处理对应中断响应。对于库代码来说有两种基本选择:

传递InterruptedException。

避开这个异常通常是最明智的选择——只需把InterruptedException传递给方法的调用者。传递InterruptedException的方法包括,根本不需要捕获该异常,或者捕获该异常,然后在执行某种简单的清理工作后再次抛出这个异常。

恢复中断。有时候不能抛出InterruptedException异常,例如代码是Runnable的一部分时。在这种情况下,必须捕获InterruptedException,并通过过调用当前线程上的interrupt方法恢复中断状态,这样在调用栈中更高层的代码将看到引发了一个中断。

还可以采用一些更复杂的中断处理方法,但上述两种方法已经可以应付大多数情况了。然而在出现InterruptedException时不应该做的事情是,捕获它但不做出任何响应。这将是调用栈上更高层的代码无法对中断采取处理措施,因为线程被中断的证据已经丢失。只有在一种特殊的情况中才能屏蔽中断,即对Thread进行扩展,并且能控制调用栈上所有更高层的代码。