Java 并发:线程中断-interrupt
一直以为执行了interrupt方法就可以让线程结束,并抛出InterruptedException. 今天看了Java并发编程实战的第七章发现并不是这么回事,在这章的开头就提到
要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的当前工作
如上提到的是协作,而不是强制。因为如果需要被中断的线程任务实现没有准守这样的协作约定,那么其他线程就没有办法通过interrupt去中断它。比如下面的foreverThread就没有对interrupt设置的boolean变量进行检测。
Thread foreverThread = new Thread(new Runnable() { @Override public void run() { int t = 0; for (int i = 0; i < 1; i++) { i--; if (t++ % 10000 == 0) { System.out.println("."); } } } }); foreverThread.start(); foreverThread.interrupt();
如书中所述,每个线程都有一个boolean类型的中断状态,我们可以通过调用线程的interrupt方法来设置这个值为true,并通过isInterrupted方法来检测这个值是否被置为true。这样当一个线程(比如主线程)通过调用另外一个(比如任务)线程interrupt方法,然后这个任务线程内有检测该boolean值是否置位的逻辑,如果为true这中止当前的工作,然后按照库方法那样抛出一个InterruptException来提示当前任务执行被中断了。因为interrupt设置这个boolean变量是线程体相关的,一次设置后它会一直存在于线程体,这样如果不把它重新置为false,那么执行后续其他会检测这个变量的操作就会立即退出抛出InterruptException。出于这样的原因,当检测到这个boolean标记被设为true后,在要抛出InterruptException之前,还要通过调用Thread.interrupted()方法来重置这个boolean标记(即设为false)以免影响后续的操作(这也表示执行这个动作的这部分代码处理了这次中断请求,至于怎么处理,一般就是上文所说的抛出InterruptException)。
阻塞库方法,例如Thread.sleep和Object.wait等,都会检测线程何时中断,并且在发现中断时提前返回。它们在响应中断时执行的操作包括:清除中断状态,抛出InterruptException,表示阻塞操作由于中断而提前结束。
线程实例的interrupt方法用于设置boolean标记,静态的Thread.interrupted方法用于清除boolean标记(即置为false,同时返回以前的boolean值)下面是一个实例:
Thread iThread = Thread.currentThread(); iThread.interrupt(); System.out.println(iThread.isInterrupted()); System.out.println(Thread.interrupted()); System.out.println(iThread.isInterrupted());
输出如下:
true true false
使用静态的interrupted时应该小心,因为它会清除当前线程的中断状态。如果在调用interrupted时返回了true,那么除非你想屏蔽这个中断,否则必须对它进行处理——可以抛出InterruptedException,或者通过再次调用interrupt来恢复中断
如果我们在检测到interrupt中断后,抛出异常却不调用interrupted清除标记,那么会影响到以后的其他可中断调用:
BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(); Thread iThread = Thread.currentThread(); iThread.interrupt(); System.out.println(iThread.isInterrupted()); try { queue.take(); } catch (InterruptedException e) { System.out.println("thread is interrupted, isInterrupted:" + iThread.isInterrupted()); }
输出:
true thread is interrupted, isInterrupted:false
运行以上代码片段,程序会立即结束而不会再queue.take()上等待。在库函数抛出InterruptException后,通过isInterrupted检测可以发现中断boolean标记已经被清除(置为false)