本文内容基本源自
Java theory and practice: Dealing with InterruptedException
Interruption机制是干什么用的?
Interruption用于一个线程“礼貌地”打断另一个线程,被打断的线程可以理会,也可以不理会。(interruption is a way of politely asking another thread to stop what it is doing if it wants to, at its convenience. ) 不过后面可以看到,不理会者,不是好人(a good citizen)
具体怎么interrupt ?
1. 打断者执行thread.interrupt()
2. 被打断者要么收到InterruptedException, 要么会发现自己的isInterrupted() == true.
注意:
1. 如果被打断者在interruption来临时正在执行sleep(), I/O读写等Blocking Method, 则会收到InterruptedException(所以这些方法一般会throws InterruptedException)
2. 如果被打断者没有执行上述Blocking Method, 则不会收到InterruptedException异常,而只能发现自己的isInterrupted() == true
3. 注意上述两种结果只能发生一种。Java Threads 3rd 这本书说这两种结果会先后发生,太误导人了。
如果不急,可以看一下下面的例子:
package player.kent.chen.learn.interrupt; import java.text.MessageFormat; public class HelloInterrupt { private static final class Sleeper implements Runnable { public void run() { while (true) { //不做任何interrupted处理,即忽略所有interruption ; } } } public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Sleeper()); t.start(); Thread.sleep(1000l); //主线程暂停一下,注意这里的sleep()跟interrup无关 t.interrupt(); //主线程打断子线程 } }
执行结果是:死循环。证明一个线程可以不理会任何interruption.
Sleeper类里的空循环不是Blocking Method,要响应interruption就得轮询isInterrupted()方法
public void run() { while (!Thread.currentThread().isInterrupted()) { ; } }
如果把Sleeper修改成调用sleep()即一个Blocking Method,那就要捕捉InterruptedException来响应Interruption
public void run() { Thread currentThread = Thread.currentThread(); try { Thread.sleep(100000l); } catch (InterruptedException e) { System.out.print("On InterruptedException: "); System.out.println(MessageFormat.format( "Is the thread interrupted? {0} ", currentThread.isInterrupted()); //打印false } }
顺便你也可以看出,发生InterruptedException后isInterrupted()仍是false
被打断者应该如何发现interruption ?
根据上面的说明可以得出结论:
1. 如果线程的操作是Blocking Method, 则应该注意InterruptedException(捕捉这个异常,或者不捕捉直接抛出)
2. 如果线程的操作不是Blocking Method, 则应该轮询thread.isInterrupted()方法
3. 如果两种操作兼有,则两种措施都用
被打断者应该如何响应interruption?
一般来说,
1. 不捕捉InterruptedException,或者捕捉、再清理现场后,最后重新抛出
2. 如果由于方法签名限制无法throws InterruptedException (比如run()方法), 那可以Thread.currentThread().interrupt(),即打断一下自身,这个方法的签名不抛异常
3. 对于非Blocking Method操作,在发现isInterrupted() == true后,应该采取同样的处理
吞掉InterruptedException或者不理会isInterrupted() == true是不太好的作法,除非你的代码没有更上层的调用者。 比如说,你的API对外宣称会sleep 5秒,你的实现是当被打断时记一下日志,然后终止方法运行。 结果调用你的API的人发现,你的API常常只sleep了2秒。