當我們去thread
類裡面找相關的介面時,發現有 個stop
方法,看上去非常適合用來終止乙個執行緒,但是這個方法上面標了個@deprecated
註解,非常明顯,這是乙個廢棄方法,不建議使用它。主要有兩個方面的原因:
因為這個方法會將執行緒直接殺掉,沒有任何喘息機會,一旦執行緒被殺死,後面的**邏輯就再也無法得到執行,而且我們無法確定執行緒關閉的時機,也就是說執行緒有可能在任何一行**突然停止執行,這是非常危險的。
假如這個執行緒正持有某個鎖,貿然將其殺死,會導致該執行緒持有的鎖馬上被釋放,而曾經被該鎖保護的資源,可能正處於一種非原子的狀態中,此時被其他執行緒訪問到,會產生不可預知的風險。
針對於第二種情況,可能不是很好理解,下面通過乙個例子,說明一下:
public class stopdemo
private static int num;
private static class changethread extends thread
@override
public void run() }}
}private static class readthread extends thread
@override
public void run() ",num);
break;}}
}}}}
首先,在main方法中,我們啟動了兩個執行緒,其中乙個是change-thread
執行緒,用於修改變數num的值,先加1等待1s後再減1,另乙個是read-thread
執行緒用於讀取共享變數num的值,如果不為0則列印日誌並退出。
由於讀寫執行緒用了同一把互斥鎖,所以對於共享變數num的讀和寫是互斥的,正常情況下寫執行緒一定是完成+1再休眠1s再-1這樣的原子操作後,才會讓釋放鎖,因此讀執行緒讀到的值一定是0,不會列印日誌也不會退出。但是,由於我們在第8行**執行了changethread.stop()
,可能導致寫執行緒將變數加1後就直接退出了,最終讀執行緒讀到的值是1而不是0,也退出迴圈。
既然stop()
不建議使用,那是否有其他辦法用來優雅的停止乙個執行緒呢?答案是必須的,那就是兩階段終止(two-phase termination)方案。兩階段終止的兩個階段分別是指:
準備階段:發出終止指令,通過設定中斷標誌,並傳送中斷訊號,「通知」目標執行緒,可以準備停止了。
執行階段:響應終止指令,接收到中斷訊號及標誌,在此基礎決定執行緒退出時機,並執行適當清理工作。
設定中斷標誌:中斷標誌的作用是標識執行緒已經中斷了,當執行緒讀到這個標識後,就可以執行退出操作了。
發出中斷訊號:光設定中斷標誌還不行,如果執行緒當前處於阻塞狀態,就算設定了中斷標誌,執行緒也無法檢測到,這時就需要我們呼叫目標執行緒的中斷方法interrupt()
,將其從阻塞狀態中喚醒,而目標執行緒通過捕獲interruptedexception
異常,來偵測這個中斷訊號。需要注意的是,執行緒類中跟中斷相關的api主要有以下三個,特別容易混淆,需要注意區分:
方法簽名
作用public void interrupt()
中斷執行緒
public static boolean interrupted()
判斷當前執行緒是否為中斷狀態並清除中斷狀態
public boolean isinterrupted()
判斷執行緒是否為中斷狀態
可以看出,兩階段終止方案相比於stop()
方案要更加優雅,打個比方,如果說stop
像是不問青紅皂白直接將罪犯就地正法,那兩階段終止方案就像是先將罪犯收押,待事情水落石出之後,再執行相應處罰,相對而言,這種方式更加人性化也很好的避免了冤假錯案的發生。
public class twophaseterminationdemo
private static class downloadthread extends thread
@override
public void run()
try
} catch (interruptedexception e) }}
public void stopme()
public boolean isterminated()}}
原因是執行緒中斷異常**獲後,它的中斷狀態已經被jvm給清除了,所以我們需要重新設定一下執行緒的中斷狀態,在第34行加上以下**
thread.currentthread().interrupt();
但是不要高興的太早,因為這種寫法並不完美,它依賴於執行緒的中斷狀態來退出執行緒,如果目標執行緒的**中呼叫了第三方類庫的介面,而這些介面在捕獲中斷異常後,清空了執行緒中斷狀態,但是沒有重置,就會導致上面描述的那種錯誤情況,所以我們需要尋求更可靠的解決方案。
上面有提到,我們不能依賴於執行緒自身的中斷狀態,那麼正確的做法應該怎麼處理呢?其實只要自定義乙個中斷標誌就行了。具體做法如下:
public class improvetwophaseterminationdemo
private static class downloadthread extends thread
@override
public void run()
try
} catch (interruptedexception e) }}
public void stopme()
public boolean isterminated()}}
可以看到,第15行加上了乙個終止標誌terminated
,呼叫stopme()
方法的時候,將terminated
設定為true,通過這個標誌,我們就可以不依賴於執行緒自身的中斷狀態,而將執行緒進行中斷了。
這篇文章主要講解了如何優雅的關閉乙個執行緒,首先我們應該避免使用stop()
方法,這種方法簡單粗暴但具有不確定性,容易造成bug,正確的做法是通過兩階段終止方案,先發出中斷請求,設定執行緒為中斷狀態,當執行緒偵測到中斷狀態後,再去執行中斷後的清理邏輯。
如何優雅地關閉乙個socket
如何優雅地關閉乙個socket 1.關閉socket時究竟做了什麼 關閉socket分為主動關閉 active closure 和被動關閉 passive closure 兩種情況。前者是指有本地主機主動發起的關閉 而後者則是指本地主機檢測到遠端主機發起關閉之後,作出回應,從而關閉整個連線。其狀態圖...
如何優雅的停止乙個執行緒
首先我們先了解下,執行緒是分為使用者執行緒和守護執行緒的,他們的區別就在 使用者執行緒當我們的主線程停止後,使用者執行緒不會隨著主線程停止。守護執行緒當我們的主線程停止後,會跟隨著主線程一起停止。一般我們建立的都是使用者執行緒,那守護執行緒有哪些呢,例如跟程式一起啟動的gc執行緒就是守護執行緒。停止...
C 如何優雅的結束乙個執行緒
有乙個無線迴圈的執行緒需要時時接收資料,如下 class eventclass event recedatasdone?invoke this,eventargs.empty public void stoprecedatas class program ec.startrecedatas syst...