什么时候要用到中断:
比如你开了生产者Producer和消费者Consumer两个线程,并用一个同步队列放置Porducer生产的和Consumer消费的产品。在Consumer中开启一个Producer线程,并且当Consumer不想消费时,可以随时结束掉Producer线程。如果不使用中断,可以使用一个boolean canceled标志,在Producer中循环检查该标志,如果该标志为true则退出。
try{
while(! cancelled){
product = Producer.produce();
blockingQueue.put(product);
}
}catch(InterruptedException){}
可是当blockingQueue被填满,Producer线程被堵塞在put()方法时,Consumer将cancelled设为true,并停止消费。在这种情况下,Producer线程将一直阻塞在put()方法处,永远检测不到cancel被置为true。
BlockingQueue.put()方法是一个阻塞库方法,它可以及时检测到线程的中断状态。因此如果在Consumer中使用interrupt()方法中断Producer,那么即使Producer阻塞在put()处了,也能及时响应中断请求,结束生产线程。
线程中断到底是什么:
“线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前工作。”
每个线程都有一个boolean类型的中断变量,并且在Thread类中有三个方法,分别是
public class Thread{
public void interrupt(){...}
public boolean isInterrupted(){...}
public static boolean interrupted(){...}
...
}
其他线程可以调用线程的interrupt()方法,通过将中断变量设为true来中断该线程;isInterrupted()方法返回线程的中断变量状态,但不会改变其状态值;而interrupted()方法会先清除中断状态(即将中断变量设为false),并返回之前的中断状态。
System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
System.out.println("thread is interrupted: " + Thread.interrupted());
System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
输出结果:
thread is interrupted: false
thread is interrupted: true
thread is interrupted: true
thread is interrupted: false
可以看到,在使用interrupted()类方法后,线程的中断状态变为false。
怎么使用中断机制
其实,中断操作(即调用线程的interrupte()方法)并不是会停止一个正在运行的线程,它只是把线程里边的中断变量设置为true,向这个线程发出一个中断请求,而线程怎么反应,则是线程自己的事情。它可以完全不理会中断,照常执行。举个栗子
public class interruptTest {
private static class Endless extends Thread{
public void run(){
int i=0;
while (i<500){
i++;
System.out.println(i);
}
}
}
public static void main(String[] args) throws InterruptedException{
Thread endless = new Endless();
endless.start();
Thread.sleep(1);
endless.interrupt();
System.out.println("main: I have interrupted endless");
}
}
这段代码的输出为:
虽然主线程试图中断endless,但它不为所动,还是把循环执行完了,正常退出。
但不推荐线程进行如此任性的操作。当线程被发现被中断时,要不然把异常抛出(停止运行,throws InterruptedException);要不然推迟中断请求(继续进行操作),但需要把中断信息传递给调用该线程的上层调用者。对于第二种情况,来看一下下面的例子:
class Consumer extends Thread{
public void run(){
boolean interrupt = false;
try {
while (true){
try {
consume(blockingQueue.take());
return;
}catch (InterruptedException e){
interrupt = true;
}
}
}finally {
if (interrupt){
Thread.currentThread().interrupt();
}
}
}
}
线程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章开头就看不明白,发现自己其实一直不懂中断到底是个什么东西,一直傻傻地认为中断和线程阻塞是一个意思。。。然后查了一些博客,并且把不懂的地方反复看了好几遍,才稍稍有些明白了。写博客其实就是捋捋自己的思路,顺便记录一下,如有错误恳请指出。