2.5 线程的中断
中断(interrupt
)表示一个线程应该停止当前所做的事而去另外一件事。通常中断是一个线程给另外一个线程发送中断信号,程序员自行决定如如何进行响应,也就是说收到中断信号后,接下来该做什么。通常情况下,线程收到中断信号后,采取的操作都是停止运行。
我们可以在一个线程对象A中调用另一个线程对象B的interrupt方法,此时线程B就会收到中断信号。
支持中断
1、在运行时代码中调用了可以抛出InterruptedException
的方法
线程如何支持中断?这依赖于线程的运行时代码是如何编写的。如果在运行时代码中调用了可以抛出InterruptedException
的方法,那么线程在接受到的中断信号就体现在这个异常上。
以下用代码进行演示:
public class InterruptDemo { public static void main(String[] args) throws InterruptedException { Thread t=new Thread(){ @Override public void run() { //预期循环10次 for (int i = 0; i <10 ; i++) { try { Thread.sleep(4000); System.out.println("自定义线程:当前时间:"+new Date().toLocaleString()); } catch (InterruptedException e) {//这个异常由sleep方法抛出 e.printStackTrace(); System.out.println("自定义线程:收到中断信号,总共循环了"+i+"次..."); //接受到中断信号时,停止运行 return; } } } }; t.start(); //主线程休眠9秒 Thread.sleep(9000); System.out.println("主线程:等待9秒后发送中断信号..."); t.interrupt(); } }
以上代码中,我们在一个自定义的线程的run方法中,预期循环10次,每一次循环打印出当前时间并休眠4秒,当收到中断信号时,停止运行。而主线程在9秒后,给线程t发送了中断信号,因此,线程t理论上只能打印出2次当前时间。
程序运行结果如下:
自定义线程:当前时间:2016-5-31 22:23:17 自定义线程:当前时间:2016-5-31 22:23:21 主线程:等待9秒后发送中断信号... 自定义线程:收到中断信号,总共循环了2次... java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at concurrency.chapter2.InterruptDemo$1.run(InterruptDemo.java:16) |
可以看到自定义线程的确是打印出两次当前时间后就停止了运行,根本原因在于,我们在收到中断信号后,在catch代码中使用了return
,结束了方法。读者可以尝试将return去掉,这个时候,即使收到了中断信号,也会继续打印10次!
2、在运行时代码中没有调用了可以抛出InterruptedException
的方法
如果在运行时代码中没有调用可以抛出中断异常的方法,那么我们必须频繁的调用Thread类的静态方法interrupted()
来判断是否收到一个中断信号。
public class InteruptDemo2 { public static void main(String[] args) throws InterruptedException { Thread t=new Thread(){ @Override public void run() { int i=0; while (true){ //每次打印前都判断是否被中断 if (!Thread.interrupted()) { i++; System.out.println("自定义线程,打印...."+i+"次"); }else{//如果被中断,停止运行 System.out.println("自定义线程:被中断..."); return; } } } }; t.start(); //主线程休眠1毫秒,以便自定义线程执行 Thread.sleep(1); System.out.println("主线程:休眠1毫秒后发送中断信号..."); t.interrupt(); } }
以上代码中,主线程休眠1毫秒,以便自定义线程可以得到执行,之后发送中断信号,注意每次运行的结果可能不一样。运行结果如下:
自定义线程,打印....1次 自定义线程,打印....2次 自定义线程,打印....3次 自定义线程,打印....4次 自定义线程,打印....5次 自定义线程,打印....6次 主线程:休眠1毫秒后发送中断信号... 自定义线程,打印....7次 自定义线程:被中断... |
可以看到,在收到中断信号后,自定义线程停止了运行。不过,为什么自定义线程t在收到中断信号后还执行了一次呢?这是因为中断信号发送的后,t线程正好运行到了打印的代码,因此只有到下一次循环的时候才会检测到中断信号,停止运行。如果你多运行几次的话,会发现每次的结果都是不一样的。
中断状态标记
中断机制的实现是通过一个标记中断状态(interrupt status
)实现的。我们通过调用某个线程对象的interrupt
方法来设置这个标记。当一个线程通过Thread的类的静态方法interrupted
判断到自己被中断后,立即会将这个状态清空。在其他的线程中,我们可以通过调用某个线程对象的isInterrupted方法判断这个线程是否被中断,但是不会中断状态清空。
按照惯例,当一个方法接受到中断信号时,应该以抛出InterruptedException的方式退出执行。
注意,这种方式很重要,在后文我们将要提供的线程池中,其停止线程池时,就通过发送中断信号。而我们提交给线程池运行的Runnable或者Callable任务代码中,必须能响应中断,否则线程池可能会无法停止。