Java的线程用于并发执行任务。之前对线程这一块,一直没搞懂的就是中断这一块,什么interrupt(), interrupted(), isInterrupted()这几个方法,InterruptedException这个异常,一直没搞明白。今天读《Java并发编程实战》里面也说到了InterruptedException,于是又到网上找资料认真读了一下,这次终于搞懂了,于是记录一下。
一.Java线程的中断
其实Java的线程并没有一个真正的状态叫做“中断”终态,也就是说线程不会因为被“中断”或者从“中断”中恢复就自动改变任何行为,中断只是一种不同线程之间进行协作的协议,是一种约定机制,而不是强制的行为。为什么这么说呢?在Java中,每一个线程都有一个内部的布尔状态位(是由JVM维护的,在jdk源代码中是看不到的),它为false时代表线程没有被中断,它为true时代表线程被中断了。但是当这个状态位在true和false之间来回变时,线程是不会自己改变任何行为的。那么它的作用是什么呢?
二.Java中操作中断的几种方法
Java中跟这个状态位直接相关的方法有如下几个:
interrupt(), interrupted(), isInterrupted。
首先说isInterrupted(),它的作用是读取中断状态位的值,以此来判断当前被调用这个方法的线程实例是否处于中断状态。如果返回true,说明被中断了,如果返回false,说明没有被中断。除此之外,它什么都不做。
其次是interrupted(),它的作用跟isInterrupted()是一样的,也是去读中断状态位的值,以此判断调用它的线程实例是否处于中断状态。但它还有一个副作用,就是每次调用的时候,都会将中断状态位重置为false。也就是说,假如线程被中断了,那么调用它会返回true。但如果你再次调用它,或者调用isInterrupted(),即使没有其他线程来中断它,那也会返回false。
最后是interrupt(),这个方法就是单纯的将调用它的线程的中断状态置为true,也就是通常说的中断了这个线程。
三.线程中断的作用
通过上述文字,我们可以看出,中断与否,其实只是修改线程的一个状态位而已,JVM并不会因此去改变线程的执行。那么中断的意义在哪里呢?答案是它是一种线程之间的协作机制。一个线程在可以对另一个线程发起中断,代表这个线程的一种请求,也就是说“如果您觉得方便的话,麻烦处理一下这个中断”,另一个线程可以选择是否关心中断状态,决定发生中断时应该如何处理。例如当一个线程一直在阻塞等待获得一个锁,这是另一个线程可以将它中断。而被正确设计的阻塞的线程,会不断的去轮询这个中断状态,发现中断时可以选择取消等待,也可以选择不理会,继续等待。
四.线程对中断的一般处理方式。
首先是线程自己轮询中断状态之后,发现自己被中断之后的处理:
1.置之不理,继续执行自己的任务,适用于一些不能被中断的任务,但有可能在执行完之后,将自身的状态置为中断,以供更高层的调用栈处理中断。
2.抛出InterruptedException。过去看书的时候,总是搞不懂这个InterruptedException是怎么抛出来的。现在明白了,其实这个异常,也是人为抛出来的,话句话说,是设计程序的人想让它抛,它才能抛。它的作用就在于,告诉捕获到它的上层调用,我被中断了。这样做往往是一种比较好的方式,因为发生中断时,往往任务本身没法处理,交给更上层调用者是一个可行的选择。
其次是捕获到InterruptedException异常的处理:
1.生吞异常,也就是捕获之后,不做任何有效的处理,包括不把自己的中断状态位再次置为中断,以让上层调用栈感知中断,也不将异常继续往上抛。这种情况可能发生在Runnable接口的run方法内,因为run方法不能抛出异常。
2.将异常上抛,如果自己无法处理中断,就将这个异常往上抛,交给有能力处理的调用者。
3.将自己的中断状态位置为中断。理由如上,是为了让上层调用栈能感知中断。
五.线程中断的应用
线程的中断在很多阻塞方法中都有应用,例如AQS中的await方法,线程池中,阻塞队列中等。从这些方法可以看出,中断一般是用于会阻塞的方法,比如阻塞等待获取锁,阻塞从队列中获取元素等。后续我会结合中断机制研究这些类的源代码。
六.示例
下面给出一个Java中断的示例[1]:
class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000000; i++) {
if (this.isInterrupted()) {
System.out.println("should be stopped and exit");
if (this.isInterrupted()) {
System.out.println("the interrupt status was not cleared, so it is interrupted");
}
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("this line is also executed. thread does not stopped");
}
}
public class InterruptTest {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
TimeUnit.MILLISECONDS.sleep(20);
thread.interrupt();
while (true) {
if (thread.isInterrupted()) {
System.out.println("main catch interrupt");
break;
}
}
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
} catch (RuntimeException e) {
System.out.println("print");
e.printStackTrace();
}
System.out.println("end!");
}
}
输出:
MyThread运行到中间的时候会被中断,并作出了结束退出循环的响应。
总结:
本文是对自己学习线程中断机制的总结,文字都是用自己的话写的,有的可能不是很专业的名词。但是这代表了自己思考过了,而不是从网上各种抄官方的定义。我感觉这样的文章更有意义一点吧,否则就千文一面了。
参考: