多線程系列提高(8)--取消與關閉

任務和線程的啓動很容易,然後讓它們運行直到結束,或者讓它們自行停止。但是,如果我們希望提前結束任務或者線程,或者是因爲用戶取消了當前操作,或者應用程序需要被快速的關閉。Java中沒有提供任何機制來安全的終止線程,但它提供了中斷,這是一種協作機制,能夠使一個線程終止另一個線程的當前工作。
這種協作式的方法是必要的,我們很少希望某個任務、線程或服務立即停止,因爲這種立即停止會使共享的數據結構處於不一致的狀態。相反,在編寫任務和服務時可以使用一種協作的方法:當需要停止時,它們首先會清除當前正在執行的工作,然後再結束。

一、中斷

線程中斷是一種協作機制,線程可以通過這種機制來通知另一個線程,告訴它在合適的或者可能的情況下停止當前工作,並轉而執行其它的工作。
每個線程都要一個boolean類型的中斷狀態。當線程中斷時,這個線程的中斷狀態將被設置爲true,在Thread中包含了中斷線程以及查詢線程中斷狀態的方法,如下面代碼所示

//Thread中的中斷方法
public class Thread{
    public void interrupt(){...}
    public boolean isInterrupted(){...}
    public static boolean interrupted(){...}
    ......

}

interrupt()方法能中斷目標線程,而isInterrupted()方法能返回目標線程的中斷狀態。靜態的interrupted方法將清除當前線程的中斷狀態,並返回它之前的值,這也是清除中斷狀態的唯一方法。

阻塞庫方法,例如Thread.sleep和Object.wait方法等,都會檢查線程何時中斷,並且在發現中斷時提前返回。它們在響應中斷時執行的操作包括:清除中斷狀態,拋出InterruptedException,表示阻塞操作由於中斷而提前結束。JVM並不能保證阻塞方法檢測到中斷的速度,但在實際情況中響應速度還是非常快的。

對中斷操作的正確理解是:它並不會真正的中斷一個正在運行的線程,而只是發出中斷請求,然後由線程在下一個合適的時刻中斷自己。(這些時刻也被稱爲取消點)。有些方法,例如wait、sleep和join等,將嚴格的處理這種請求,當它們收到中斷請求或者在開始執行時發現某個已被設置好的中斷狀態時,將拋出一個異常。設計良好的方法可以完全忽略這種請求,只要它們能使調用代碼對中斷請求進行某種處理。設計糟糕的方法可能會屏蔽中斷請求,從而導致調用棧中國的其它代碼無法對中斷請求做出響應。
通常,中斷是實現取消的最合理的方式。

二、中斷策略

中斷線程規定線程如何解釋某個中斷請求–當發現中斷請求時,應該做哪些工作(如果需要的話),哪些工作單元對於中斷來說是原子操作,以及以多快的速度來響應中斷。
最合理的中斷策略是某種形式的線程級(Thread-Level)取消操作或服務級(Service-Level)取消操作:儘快退出,在必要時進行清理,通知某個所有者該線程已經退出。此外還可以建立其它的中斷策略,例如暫停服務或重新開始服務,但對於那些包含非標準中斷策略的線程或線程池,只能用於能知道這些策略的任務中。
當檢查到中斷時,任務並需要放棄所有的操作–它可以推遲處理中斷請求,並直到某個更合適的時刻,因此需要記住中斷請求,並在完成當前任務後拋出InterruptedException或者表示已收到中斷請求。這項技術能夠確保在更新過程中發生中斷時,數據結構不會被破壞。
如果除了將InterruptedException傳遞給調用者外還需要執行其它操作,那麼應該在捕獲InterruptedException之後恢復中斷狀態:

Thread.currentThread().interrupt();

三、響應中斷

當調用可中斷的阻塞函數時,例如Thread.sleep或BlockingQueue.put等,有兩種實用策略可用於處理InterruptedException:
傳遞異常:可能在執行某個特定於任務的清除操作之後,從而使你的方法也成爲可中斷的阻塞方法。
恢復中斷方法:從而使調用棧中的上層代碼能夠對其進行處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章