不學無數——InterruptedException異常

InterruptedException異常

在瞭解InterruptedException異常以前應該瞭解如下的幾個關於線程的一些基礎知識。html

線程的狀態

線程在必定的條件下會發生狀態的改變,下面是線程的一些狀態java

<center><img src="http://p9jfgo4wc.bkt.clouddn.com/thread.png"/></center>學習

  • 初始(NEW):新建一個線程的對象,還未調用start方法
  • 運行(RUNNABLE):java線程中將已經準備就緒(Ready)和正在運行中(Running)的兩種狀態都統稱爲「Runnable」。準備就緒的線程會被放在線程池中等待被調用
  • 阻塞(BLOCKED):是由於某種的緣由而放棄了CPU的使用權,暫時的中止了運行。直到線程進入準備就緒(Ready)狀態纔會有機會轉到運行狀態
  • 等待(WAITING):該狀態的線程須要等待其餘線程作出一些特定的動做(通知或者是中斷)
  • 超時等待(TIME_WAITING):該狀態和上面的等待不一樣,他能夠在指定的時間內自行返回
  • 終止(TERMINATED):線程任務執行完畢

而InterruptedException異常從字面意思上就是中斷異常,那麼什麼是中斷呢?學習中斷以前咱們先了解一下具體什麼是阻塞ui

線程阻塞

線程阻塞一般是指一個線程在執行過程當中暫停,以等待某個條件的觸發。而什麼狀況纔會使得線程進入阻塞的狀態呢?this

  • 等待阻塞:運行的線程執行wait()方法,該線程會釋放佔用的全部資源,JVM會把該線程放入「等待池」中。進入這個狀態後,是不能自動喚醒的,必須依靠其餘線程調用notify()或notifyAll()方法才能被喚醒
  • 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入「鎖池」中
  • 其餘阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態

線程中斷

若是咱們有一個運行中的軟件,例如是殺毒軟件正在全盤查殺病毒,此時咱們不想讓他殺毒,這時候點擊取消,那麼就是正在中斷一個運行的線程。.net

每個線程都有一個boolean類型的標誌,此標誌意思是當前的請求是否請求中斷,默認爲false。當一個線程A調用了線程B的interrupt方法時,那麼線程B的是否請求的中斷標誌變爲true。而線程B能夠調用方法檢測到此標誌的變化。線程

  1. 阻塞方法:若是線程B調用了阻塞方法,若是是否請求中斷標誌變爲了true,那麼它會拋出InterruptedException異常。拋出異常的同時它會將線程B的是否請求中斷標誌置爲false
  2. 非阻塞方法:能夠經過線程B的isInterrupted方法進行檢測是否請求中斷標誌爲true仍是false,另外還有一個靜態的方法interrupted方法也能夠檢測標誌。可是靜態方法它檢測完之後會自動的將是否請求中斷標誌位置爲false。例如線程A調用了線程B的interrupt的方法,那麼若是此時線程B中用靜態interrupted方法進行檢測標誌位的變化的話,那麼第一次爲true,第二次就爲false。下面爲具體的例子:
/**
 * @program: Test
 * @description:
 * @author: hu_pf@suixingpay.com
 * @create: 2018-07-31 15:43
 **/
public class InterrupTest implements Runnable{

    public void run(){
            try {
                while (true) {
                    Boolean a = Thread.currentThread().isInterrupted();
                    System.out.println("in run() - about to sleep for 20 seconds-------" + a);
                    Thread.sleep(20000);
                    System.out.println("in run() - woke up");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();//若是不加上這一句,那麼cd將會都是false,由於在捕捉到InterruptedException異常的時候就會自動的中斷標誌置爲了false
                Boolean c=Thread.interrupted();
                Boolean d=Thread.interrupted();
                System.out.println("c="+c);
                System.out.println("d="+d);
            }
    }
    public static void main(String[] args) {
        InterrupTest si = new InterrupTest();
        Thread t = new Thread(si);
        t.start();
        //主線程休眠2秒,從而確保剛纔啓動的線程有機會執行一段時間
        try {
            Thread.sleep(2000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("in main() - interrupting other thread");
        //中斷線程t
        t.interrupt();
        System.out.println("in main() - leaving");
    }
}

打印的參數以下:日誌

in run() - about to sleep for 20 seconds-------false
in main() - interrupting other thread
in main() - leaving
c=true
d=false

如今知道線程能夠檢測到自身的標誌位的變化,可是他只是一個標誌,若是線程自己不處理的話,那麼程序仍是會執行下去,就比如,老師在學校叮囑要好好學習,具體何時,如何好好學習仍是看自身。code

所以interrupt() 方法並不能當即中斷線程,該方法僅僅告訴線程外部已經有中斷請求,至因而否中斷還取決於線程本身htm

InterruptedException異常的處理

簡單的瞭解了什麼是阻塞和中斷之後,咱們就該瞭解碰到InterruptedException異常該如何處理了。

不要無論不顧

有時候阻塞的方法拋出InterruptedException異常並不合適,例如在Runnable中調用了可中斷的方法,由於你的程序是實現了Runnable接口,而後在重寫Runnable接口的run方法的時候,那麼子類拋出的異常要小於等於父類的異常。而在Runnable中run方法是沒有拋異常的。因此此時是不能拋出InterruptedException異常。若是此時你只是記錄日誌的話,那麼就是一個不負責任的作法,由於在捕獲InterruptedException異常的時候自動的將是否請求中斷標誌置爲了false。至少在捕獲了InterruptedException異常以後,若是你什麼也不想作,那麼就將標誌從新置爲true,以便棧中更高層的代碼能知道中斷,而且對中斷做出響應。

捕獲到InterruptedException異常後恢復中斷狀態

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;
 
    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }
 
    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         }
         catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

參考文章

相關文章
相關標籤/搜索