理解Java Interruption机制

本文内容基本源自
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秒。

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.