多線程編程讀書筆記之線程中斷的本質

 

Java試圖提供過搶佔式限制中斷,但問題多多,例如已被廢棄的Thread.stop、Thread.suspend和 Thread.resume等。另外一方面,出於Java應用代碼的健壯性的考慮,下降了編程門檻,減小不清楚底層機制的程序員無心破壞系統的機率。
現在,Java的線程調度不提供搶佔式中斷,而採用協做式的中斷。其實,協做式的中斷,原理很簡單,就是輪詢某個表示中斷的標記,咱們在任何普通代碼的中均可以實現。 例以下面的代碼:

volatile bool isInterrupted;
//…
while(!isInterrupted) {
compute();
}

可是,上述的代碼問題也很明顯。當compute執行時間比較長時,中斷沒法及時被響應。另外一方面,利用輪詢檢查標誌變量的方式,想要中斷wait和sleep等線程阻塞操做也一籌莫展。

若是仍然利用上面的思路,要想讓中斷及時被響應,必須在虛擬機底層進行線程調度的對標記變量進行檢查。是的,JVM中確實是這樣作的。下面摘自java.lang.Thread的源代碼:

public static boolean interrupted() {
return currentThread().isInterrupted(true);
}

//…

private native boolean isInterrupted(boolean ClearInterrupted);

能夠發現,isInterrupted被聲明爲native方法,取決於JVM底層的實現。

實際上,JVM內部確實爲每一個線程維護了一箇中斷標記。但應用程序不能直接訪問這個中斷變量,必須經過下面幾個方法進行操做:
public class Thread {
//設置中斷標記
public void interrupt() { ... }
//獲取中斷標記的值
public boolean isInterrupted() { ... }
//清除中斷標記,並返回上一次中斷標記的值
public static boolean interrupted() { ... }
...
}

一般狀況下,調用線程的interrupt方法,並不能當即引起中斷,只是設置了JVM內部的中斷標記。所以,經過檢查中斷標記,應用程序能夠作一些特殊操做,也能夠徹底忽略中斷。

你可能想,若是JVM只提供了這種簡陋的中斷機制,那和應用程序本身定義中斷變量並輪詢的方法相比,基本也沒有什麼優點。

JVM內部中斷變量的主要優點,就是對於某些狀況,提供了模擬自動「中斷陷入」的機制。

在執行涉及線程調度的阻塞調用時(例如wait、sleep和join),若是發生中斷,被阻塞線程會「儘量快的」拋出InterruptedException。所以,咱們就能夠用下面的代碼框架來處理線程阻塞中斷:
try {
//wait、sleep或join
}
catch(InterruptedException e) {
//某些中斷處理工做
}
所謂「儘量快」,我猜想JVM就是在線程調度調度的間隙檢查中斷變量,速度取決於JVM的實現和硬件的性能。

然而,對於某些線程阻塞操做,JVM並不會自動拋出InterruptedException異常。例如,某些I/O操做和內部鎖操做。對於這類操做,能夠用其餘方式模擬中斷:
1)java.io中的異步socket I/O
讀寫socket的時候,InputStream和OutputStream的read和write方法會阻塞等待,但不會響應java中斷。不過,調用Socket的close方法後,被阻塞線程會拋出SocketException異常。

2)利用Selector實現的異步I/O
若是線程被阻塞於Selector.select(在java.nio.channels中),調用wakeup方法會引發ClosedSelectorException異常。

3)鎖獲取
若是線程在等待獲取一個內部鎖,咱們將沒法中斷它。可是,利用Lock類的lockInterruptibly方法,咱們能夠在等待鎖的同時,提供中斷能力。


另外,在任務與線程分離的框架中,任務一般並不知道自身會被哪一個線程調用,也就不知道調用線程處理中斷的策略。因此,在任務設置了線程中斷標記後,並不能確保任務會被取消。所以,有如下兩條編程原則:
1)除非你知道線程的中斷策略,不然不該該中斷它。
這條原則告訴咱們,不該該直接調用Executer之類框架中線程的interrupt方法,應該利用諸如Future.cancel的方法來取消任務。

2)任務代碼不應猜想中斷對執行線程的含義。
這條原則告訴咱們,通常代碼遇在到InterruptedException異常時,不該該將其捕獲後「吞掉」,而應該繼續向上層代碼拋出。

總之,Java中的非搶佔式中斷機制,要求咱們必須改變傳統的搶佔式中斷思路,在理解其本質的基礎上,採用相應的原則和模式來編程。
java

相關文章
相關標籤/搜索