深入Java線程中斷的本質與編程原則的概述_java

來源:互聯網
上載者:User
在曆史上,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中的非搶佔式中斷機制,要求我們必須改變傳統的搶佔式中斷思路,在理解其本質的基礎上,採用相應的原則和模式來編程。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.