版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 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()調用。
它會做兩步操作:
- 傳回目前線程的中斷狀态;
- 将目前線程的中斷狀态設為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
- 為什麼catch InterruptedException後會自動清除中斷狀态?