什么时候要用到中断:
比如你开了生产者Producer和消费者Consumer两个线程,并用一个同步队列放置Porducer生产的和Consumer消费的产品。在Consumer中开启一个Producer线程,并且当Consumer不想消费时,可以随时结束掉Producer线程。如果不使用中断,可以使用一个boolean canceled标志,在Producer中循环检查该标志,如果该标志为true则退出。
1try{ 2 while(! cancelled){ 3 product = Producer.produce(); 4 blockingQueue.put(product); 5 } 6}catch(InterruptedException){}
可是当blockingQueue被填满,Producer线程被堵塞在put()方法时,Consumer将cancelled设为true,并停止消费。在这种情况下,Producer线程将一直阻塞在put()方法处,永远检测不到cancel被置为true。
BlockingQueue.put()方法是一个阻塞库方法,它可以及时检测到线程的中断状态。因此如果在Consumer中使用interrupt()方法中断Producer,那么即使Producer阻塞在put()处了,也能及时响应中断请求,结束生产线程。
线程中断到底是什么:
“线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前工作。”
每个线程都有一个boolean类型的中断变量,并且在Thread类中有三个方法,分别是
1public class Thread{ 2 public void interrupt(){...} 3 public boolean isInterrupted(){...} 4 public static boolean interrupted(){...} 5 ... 6}
其他线程可以调用线程的interrupt()方法,通过将中断变量设为true来中断该线程;isInterrupted()方法返回线程的中断变量状态,但不会改变其状态值;而interrupted()方法会先清除中断状态(即将中断变量设为false),并返回之前的中断状态。
1System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted()); 2 Thread.currentThread().interrupt(); 3 System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted()); 4 System.out.println("thread is interrupted: " + Thread.interrupted()); 5 System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
输出结果:
1thread is interrupted: false 2thread is interrupted: true 3thread is interrupted: true 4thread is interrupted: false
可以看到,在使用interrupted()类方法后,线程的中断状态变为false。
怎么使用中断机制
其实,中断操作(即调用线程的interrupte()方法)并不是会停止一个正在运行的线程,它只是把线程里边的中断变量设置为true,向这个线程发出一个中断请求,而线程怎么反应,则是线程自己的事情。它可以完全不理会中断,照常执行。举个栗子
1public class interruptTest { 2 private static class Endless extends Thread{ 3 public void run(){ 4 int i=0; 5 while (i<500){ 6 i++; 7 System.out.println(i); 8 } 9 } 10 } 11 public static void main(String[] args) throws InterruptedException{ 12 Thread endless = new Endless(); 13 endless.start(); 14 Thread.sleep(1); 15 endless.interrupt(); 16 System.out.println("main: I have interrupted endless"); 17 } 18}
这段代码的输出为:


虽然主线程试图中断endless,但它不为所动,还是把循环执行完了,正常退出。
但不推荐线程进行如此任性的操作。当线程被发现被中断时,**要不然把异常抛出(停止运行,throws InterruptedException);要不然推迟中断请求(继续进行操作),但需要把中断信息传递给调用该线程的上层调用者。**对于第二种情况,来看一下下面的例子:
1class Consumer extends Thread{ 2 public void run(){ 3 boolean interrupt = false; 4 try { 5 while (true){ 6 try { 7 consume(blockingQueue.take()); 8 return; 9 }catch (InterruptedException e){ 10 interrupt = true; 11 } 12 } 13 }finally { 14 if (interrupt){ 15 Thread.currentThread().interrupt(); 16 } 17 18 } 19 } 20}
线程Consumer会执着地等到消费了一个从阻塞队列中拿到的产品后,才结束执行,即使该线程的中断状态被设为true,且被blockingQueue.take()方法发现,并抛出InterruptedException后。但它在finally中将线程的中断状态重新置为了true,以保证调用Consumer的上层线程能知道该线程被中断过。
但现在出现了一个问题是,**为什么在finally中要调用interrupt()方法重新把线程中断状态置为true呢。**讲道理如果blockingQueue.take()方法发现了中断,那就证明线程中断状态已经是true了啊。原因就在于这个blockingQueue的take()方法。在阻塞库方法,如Thread.sleep(), Object.wait(),以及这里的BlockingQueue.take()方法中(其实从BlockingQueue.take()的源码中可以看到,在take()方法中调用了Object.wait()),都及时地响应当前线程的中断请求,并且在响应时执行两个操作:一是清除中断状态,二是抛出InterruptedException。清除中断状态就是把中断状态重新置为false,这就解释了为什么在finally中要重新调用interrupt()方法。
什么是恢复中断
现在可以来解释一下什么叫恢复中断,我理解的恢复中断,并不是把线程从BLOCKED或WAITING等状态中解救出来(本来中断也不是这么个意思),而是把线程的中断状态变量重新设置为True。
写在后面:
看《Java并发编程实战》时看到第7章开头就看不明白,发现自己其实一直不懂中断到底是个什么东西,一直傻傻地认为中断和线程阻塞是一个意思。。。然后查了一些博客,并且把不懂的地方反复看了好几遍,才稍稍有些明白了。写博客其实就是捋捋自己的思路,顺便记录一下,如有错误恳请指出。