InterruptedException简单描述
InterruptedException异常是一个经常被误解的异常,通常情况下我们会忽视或则转化成RuntimeException并抛出:throw new RuntimeException(e);然而这种做法通常都是不正确的。
InterruptedException出现一般都是因为在线程执行的时候被打断(interrupt),线程(A)不是自己打断自己,一般都是在另外一个线程(B)中执行中断方法(objA.interrupt())。
每个线程都管理一个自己的中断状态(interruption status),它可以通过Thread#interrupt()方法设置位中断状态,或则使Thread#interrupted()重置状态。另外,Thread#isInterrupted()通常用来判断是否处以中断状态。
程序的状态
RUNNABLE: 程序正在运行,其他线程能够调度它.
BLOCKED, WAITING or TIMED_WAITING:线程等待中,等待一些像超时等事件的发生.
当线程处于RUNNABLE状态的时候,通过Thread.currentThread.isInterrupted()判断中断状态,并终止程序循环执行:
class ComplicatedCalculator implements Runnable {
@Override
public void run() {
while (!Thread.currentThread.isInterrupted()) {
// calculate something here
}
}
}
当线程处在BLOCKED, WAITING or TIMED_WAITING状态的时候,考虑以下程序,如何判断sleep正常执行完而没有被中断,也许可以通过sleep返回一个状态来判断,但是这增加了处理判断该状态的负担,所以sleep设计成了当抛出InterruptedException异常的方式来处理中断(这里拿Thread.sleep做示例,其他的也一样)。
class Simulation implements Runnable {
@Override
public void run() {
while (!Thread.currentThread.isInterrupted()) {
// ... simulation calculations here ...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// empty
}
}
}
}
处理InterruptedException异常的常用方法:
- Rethrow:重新抛出,一般对blocking结果操作(如:BlockingQueue)直接重新抛出。
class BlockQueueTest {
ArrayBlockingQueue<Integer> testQueue = new ArrayBlockingQueue<Integer>(4);
public int poll() throws InterruptedException {
return testQueue.poll(10, TimeUnit.SECONDS);
}
public void put() throws InterruptedException {
testQueue.put(1);
}
}
- Catch, restore and terminate:像Runnable这种不能Rethrow异常,通常做法是catch异常,然后恢复中断状态,然后立即终止。
注意: sleep被打断后会重置中断状态并抛出InterruptedException异常,也就是说当sleep catch住的代码块中,当前线程已经不是中断状态了,如果要终止程序,需要调用Thread.currentThread.interrupt(),Thread.currentThread.interrupt()就是将线程restore的意思了。
class Simulation implements Runnable {
@Override
public void run() {
while (!Thread.currentThread.isInterrupted()) {
// ... simulation calculations here ...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// restore interruption status of the corresponding thread
Thread.currentThread.interrupt();
}
}
}
}
- Catch and terminate:这是上面一种方式的变种,像Thread的子类,已经是调用栈(不太懂)的最上层调用,没有人关系它的中中断状态,可以直接终止进程了。
以下程序是我猜测这种处理方式的意思(不知道对不对):
class Simulation extend Thread {
@Override
public void run() {
while (true) {
// ... simulation calculations here ...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// terminate
return;
}
}
}
}
个人感言(可以忽略):在方法或代码出现中断异常,如果不会对后面程序照成影响的话,我一般都记warn级别的日志(不知道对不对)。
线程池中断线程
通常情况下会通过调用Future#cancel(true)来发送中断请求。
ExecutorService e = Executors.newFixedThreadPool(4);
Future<?> f = e.submit(simulation);
// ...
// now cancel the running simulation
f.cancel(true);
参考文献
http://daniel.mitterdorfer.name/articles/2015/handling-interruptedexception/