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/
