中断是一种协作机制,一个线程不能强制其他线程停止正在执行的操作而去执行其他操作。
什么是中断状态?
线程类有一个描述自身是否被中断了的boolean类型的状态,可以通过调用 .isInterrupted() 方法来查看。官方解释如下:
简单来说,这个方法如果返回 true ,那么表示线程已经被中断。
恢复中断
这里提到的 “恢复中断” 绝对不是 “从中断中恢复” 的意思!
在《Java并发编程实战》一书第五章 p78页,首次提到了这个恢复中断的概念,而当我看到第七章的时候才明白这里为什么要恢复中断。
因为中断是一种协作机制,当一个线程阻塞时,我们需要通过另一个线程调用阻塞线程的 interrupt() 方法,来请求阻塞线程取消任务。最简单的阻塞方法就是 Thread.sleep() , 我们来看一下我写的一个简单的例子:
public class Demo_01 {public static void main(String[] args) throws InterruptedException {Thread myTh = new Thread(new TaskDemo());myTh.start();TimeUnit.SECONDS.sleep(3);// 中断myTh线程myTh.interrupt();}
}class TaskDemo implements Runnable {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {System.out.println("捕获到中断异常,当前线程的中断状态是:" + Thread.currentThread().isInterrupted());System.out.println("即将恢复中断... ...");Thread.currentThread().interrupt();System.out.println("中断恢复!中断状态已经是:" + Thread.currentThread().isInterrupted());}}
}
执行结果是:
我们可以看到,interrupt()方法会将线程的中断状态置为true,而任务被中断的时候不仅会抛出一个InterruptedException异常,还会清除中断状态!
这个问题在书中第七章 P114 有所说明:
阻塞库方法,例如Thread.sleep和Object.wait等,都会检查线程何时中断,并且在发现中断时提前返回。它们在响应中断时执行的操作包括:清除中断状态;抛出InterruptedException,表示阻塞操作由于中断而提前结束。
上面的程序中,由于TaskDemo实现Runnable接口,在面对中断请求的时候,无法向上层抛出,必须捕获,如果没有在catch块中调用interrupt()方法,那么中断状态将在清除之后被上层逻辑 “屏蔽”。书中强调,一个健壮的任务执行方法,应该能够正确处理中断请求,而不是屏蔽。
因此,如果不想任务执行时出现的中断被屏蔽,就必须将线程的中断状态恢复,换句话说,就是再次将线程的中断状态置为true!
因为有点绕,所以大多数人可能完全无视了这个问题,导致我在搜索这个恢复中断的问题时,页面里全都是些如何从中断的状态中恢复过来,真的很无语。
总结
调用线程的interrupt()方法可以向一个阻塞中的线程发出中断请求,其原理就是将线程的中断状态设置为true。
阻塞的线程在收到中断请求的时候,会执行两步操作:清除中断状态,抛出中断异常(InterruptedException)。
这就导致了在一些无法向调用者继续抛出中断异常的情况下,如实现了Runnable接口,必须在run()方法内部进行中断异常的捕获,从而导致了上层调用者可能无法正确获得线程的中断状态,导致没有相应的处理措施,即为中断被屏蔽。
因此,在这种情况下,为了避免任务内部屏蔽了中断请求(注意,屏蔽中断请求并不意味着任务没有响应中断),需要通过在catch块中重新调用任务所在线程的interrupt()方法,恢复到中断状态,来告诉上层调用者,“我是一个已经被中断的线程”。
综上,就是关于《Java并发编程实战》中“恢复中断”的相关理解,欢迎文末留言。