Web全堆疊~33.執行緒的中斷
上一期
取消/關閉的場景
一般執行緒通過start方法啟動之后,就會開始執行run方法,run方法運行結束后執行緒退出,但是呢,又有一些場景可能會需要我們自己讓它提前退出,
很多執行緒的運行模式是死回圈,就說上一期的生產者/消費者模式中,消費者主體就是一個死回圈,它不停地從佇列中接受任務,執行任務,在停止程式時,我們需要一種“優雅”的方法以關閉該執行緒,
再比如說下載,當用戶想要遠程從服務器中下載檔案的時候,很多時候都會需要一個取消下載的功能等等等...
在Java中,停止一個執行緒的主要機制是中斷,中斷并不是強迫終止一個執行緒,它是一種協作機制,是給執行緒傳遞一個取消信號,但是由執行緒來決定如何以及何時退出,
public boolean isInterrupted()
public void interrupt()
public static boolean interrupted()
isInterrupted()和interrupt()是實體方法,呼叫它們需要通過執行緒物件;interrupted()是靜態方法,實際會呼叫Thread.currentThread()操作當前執行緒,
每個執行緒都有一個標志位,表示該執行緒是否被中斷了,
isInterrupted:回傳對應執行緒的中斷標志位是否為true,
interrupted:回傳當前執行緒的中斷標志位是否為true,但它還有一個重要的副作用,就是清空中斷標志位,也就是說,連續兩次呼叫interrupted(),第一次回傳的結果為true,第二次一般就是false(除非同時又發生了一次中斷),
interrupt:表示中斷對應的執行緒,
執行緒的狀態
interrupt()對執行緒的影響與執行緒的狀態和在進行的IO操作有關,而IO操作的影響和具體IO以及作業系統有關,
RUNNABLE:執行緒在運行或具備運行條件只是在等待作業系統調度,
WAITING/TIMED_WAITING:執行緒在等待某個條件或超時,
BLOCKED:執行緒在等待鎖,試圖進入同步塊,
NEW/TERMINATED:執行緒還未啟動或已結束,
RUNNABLE
如果執行緒在運行中,且沒有執行IO操作,interrupt()只是會設定執行緒的中斷標志位,沒有任何其他作用,執行緒應該在運行程序中合適的位置檢查中斷標志位,比如,如果主體代碼是一個回圈,可以在回圈開始處進行檢查
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("回圈中...");
}
System.out.println("done");
}
WAITING/TIMED_WAITING
執行緒呼叫join/wait/sleep方法會進入WAITING或TIMED_WAITING狀態,在這些狀態時,對執行緒物件呼叫interrupt()會使得該執行緒拋出InterruptedException,需要注意的是,拋出例外后,中斷標志位會被清空,而不是被設定,
public class Test extends Thread{
@Override
public void run() {
try{
Thread.sleep(100);
}catch (InterruptedException interruptedException){
System.out.println(isInterrupted());
}
}
public static void main(String[] args){
Thread thread = new Test();
thread.start();
try{
Thread.sleep(100);
}catch (InterruptedException interruptedException){
}
thread.interrupt();
}
}
捕獲到InterruptedException,通常表示希望結束該執行緒
向上傳遞該異常,這使得該方法也變成了一個可中斷的方法,需要呼叫者進行處理,
有些情況,不能向上傳遞例外,比如Thread的run方法,它的宣告是固定的,不能拋出任何受檢例外,這時,應該捕獲例外,進行合適的清理操作,清理后,一般應該呼叫Thread的interrupt方法設定中斷標志位,使得其他代碼有辦法知道它發生了中斷,
BLOCKED
如果執行緒在等待鎖,對執行緒物件呼叫interrupt()只是會設定執行緒的中斷標志位,執行緒依然會處于BLOCKED狀態,也就是說,interrupt()并不能使一個在等待鎖的執行緒真正“中斷”,
public class Test extends Thread{
private static Object lock = new Object();
private static class A extends Thread {
@Override
public void run() {
synchronized (lock) {
while (!Thread.currentThread().isInterrupted()) {
}
}
System.out.println("exit");
}
}
public static void test() throws InterruptedException {
synchronized (lock) {
A a = new A();
a.start();
Thread.sleep(1000);
a.interrupt();
a.join();
}
}
public static void main(String[] args) throws InterruptedException {
test();
}
}
test方法在持有鎖lock的情況下啟動執行緒a,而執行緒a也去嘗試獲得鎖lock,所以會進入鎖等待佇列,隨后test呼叫執行緒a的interrupt方法并呼叫join等待執行緒執行緒a結束,執行緒a會結束嗎?不會,interrupt方法只會設定執行緒的中斷標志,而并不會使它從鎖等待佇列中出來,
在使用synchronized關鍵字獲取鎖的程序中不回應中斷請求,這是synchronized的局限性,如果這對程式是一個問題,應該使用顯式鎖,第16章會介紹顯式鎖Lock介面,它支持以回應中斷的方式獲取鎖,
NEW/TERMINATED
如果執行緒尚未啟動(NEW),或者已經結束(TERMINATED),則呼叫interrupt()對它沒有任何效果,中斷標志位也不會被設定,
總結
interrupt方法不一定會真正“中斷”執行緒,它只是一種協作機制,如果不明白執行緒在做什么,不應該貿然地呼叫執行緒的interrupt方法,以為這樣就能取消執行緒,對于以執行緒提供服務的程式模塊而言,它應該封裝取消/關閉操作,提供單獨的取消/關閉方法給呼叫者,外部呼叫者應該呼叫這些方法而不是直接呼叫interrupt,
Java并發庫的一些代碼就提供了單獨的取消/關閉方法,比如,Future介面提供了如下方法以取消任務,再如,ExecutorService提供了如下兩個關閉方法,
boolean cancel(boolean mayInterruptIfRunning);
void shutdown();
List<Runnable> shutdownNow();
Future和ExecutorService的API檔案對這些方法都進行了詳細說明
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/260042.html
標籤:java
下一篇:Python04--集合
