天天看點

Java并發程式設計的藝術(五)——中斷什麼是中斷? 中斷的相關方法 暫停、繼續、停止線程(已過時) 中斷的使用 安全地停止線程 進行中斷

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qq_34173549/article/details/79612424

什麼是中斷?

在Java中沒有辦法立即停止一條線程,然而停止線程卻顯得尤為重要,如取消一個耗時操作。是以,Java提供了一種用于停止線程的機制——中斷。

  • 中斷隻是一種協作機制,Java沒有給中斷增加任何文法,中斷的過程完全需要程式員自己實作。若要中斷一個線程,你需要手動調用該線程的interrupted方法,該方法也僅僅是将線程對象的中斷辨別設成true;接着你需要自己寫代碼不斷地檢測目前線程的辨別位;如果為true,表示别的線程要求這條線程中斷,此時究竟該做什麼需要你自己寫代碼實作。
  • 每個線程對象中都有一個辨別,用于表示線程是否被中斷;該辨別位為true表示中斷,為false表示未中斷;
  • 通過調用線程對象的interrupt方法将該線程的辨別位設為true;可以在别的線程中調用,也可以在自己的線程中調用。

中斷的相關方法

  • public void interrupt() 

    将調用者線程的中斷狀态設為true。

  • public boolean isInterrupted() 

    判斷調用者線程的中斷狀态。

  • public static boolean interrupted 

    隻能通過Thread.interrupted()調用。 

    它會做兩步操作:

    1. 傳回目前線程的中斷狀态;
    2. 将目前線程的中斷狀态設為false;

暫停、繼續、停止線程(已過時)

以下三個方法都是通過線程對象去調用。

  • suspend() 

    暫停調用者線程,隻釋放CPU執行權,不釋放鎖。 

    由于在不釋放資源的情況下進入睡眠狀态,容易産生死鎖。是以已過時!

  • resume() 

    恢複調用者線程,讓他處于就緒狀态。

  • stop() 

    調用stop後,并不會保證資源被正确地釋放,它會使程式處于不正确的狀态下。

PS:stop和interrupt的差別?

中斷的使用

要使用中斷,首先需要在可能會發生中斷的線程中不斷監聽中斷狀态,一旦發生中斷,就執行相應的中斷處理代碼。 

當需要中斷線程時,調用該線程對象的interrupt函數即可。

設定中斷監聽

Thread t1 = new Thread( new Runnable(){
    public void run(){
        // 若未發生中斷,就正常執行任務
        while(!Thread.currentThread.isInterrupted()){
            // 正常任務代碼……
        }

        // 中斷的處理代碼……
        doSomething();
    }
} ).start();           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

正常的任務代碼被封裝在while循環中,每次執行完一遍任務代碼就檢查一下中斷狀态;一旦發生中斷,則跳過while循環,直接執行後面的中斷處理代碼。

觸發中斷

t1.interrupt();           

上述代碼執行後會将t1對象的中斷狀态設為true,此時t1線程的正常任務代碼執行完成後,進入下一次while循環前Thread.currentThread.isInterrupted()的結果為true,此時退出循環,執行循環後面的中斷處理代碼。

安全地停止線程

stop函數停止線程過于暴力,它會立即停止線程,不給任何資源釋放的餘地,下面介紹兩種安全停止線程的方法。

循環标記變量

自定義一個共享的boolean類型變量,表示目前線程是否需要中斷。

  • 中斷辨別
volatile boolean interrupted = false;           
  • 任務執行函數
Thread t1 = new Thread( new Runnable(){
    public void run(){
        while(!interrupted){
            // 正常任務代碼……
        }
        // 中斷處理代碼……
        // 可以在這裡進行資源的釋放等操作……
    }
} );           
  • 中斷函數
Thread t2 = new Thread( new Runnable(){
    public void run(){
        interrupted = true;
    }
} );           

循環中斷狀态

  • 中斷辨別 

    由線程對象提供,無需自己定義。

Thread t1 = new Thread( new Runnable(){
    public void run(){
        while(!Thread.currentThread.isInterrupted()){
            // 正常任務代碼……
        }
        // 中斷處理代碼……
        // 可以在這裡進行資源的釋放等操作……
    }
} );           
t1.interrupt();           

總結

上述兩種方法本質一樣,都是通過循環檢視一個共享标記為來判斷線程是否需要中斷,他們的差別在于:第一種方法的辨別位是我們自己設定的,而第二種方法的辨別位是Java提供的。除此之外,他們的實作方法是一樣的。

上述兩種方法之是以較為安全,是因為一條線程發出終止信号後,接收線程并不會立即停止,而是将本次循環的任務執行完,再跳出循環停止線程。此外,程式員又可以在跳出循環後添加額外的代碼進行收尾工作。

進行中斷

上文都在介紹如何擷取中斷狀态,那麼當我們捕獲到中斷狀态後,究竟如何處理呢?

  • Java類庫中提供的一些可能會發生阻塞的方法都會抛InterruptedException異常,如:BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep。
  • 當你在某一條線程中調用這些方法時,這個方法可能會被阻塞很長時間,你可以在别的線程中調用目前線程對象的interrupt方法觸發這些函數抛出InterruptedException異常。
  • 當一個函數抛出InterruptedException異常時,表示這個方法阻塞的時間太久了,别人不想等它執行結束了。
  • 當你的捕獲到一個InterruptedException異常後,亦可以處理它,或者向上抛出。
  • 抛出時要注意???:當你捕獲到InterruptedException異常後,目前線程的中斷狀态已經被修改為false(表示線程未被中斷);此時你若能夠進行中斷,則不用理會該值;但如果你繼續向上抛InterruptedException異常,你需要再次調用interrupt方法,将目前線程的中斷狀态設為true。
  • 注意:絕對不能“吞掉中斷”!即捕獲了InterruptedException而不作任何處理。這樣違背了中斷機制的規則,别人想讓你線程中斷,然而你自己不處理,也不将中斷請求告訴調用者,調用者一直以為沒有中斷請求。

QA

  1. 為什麼catch InterruptedException後會自動清除中斷狀态?