淺析在Java中如何優(yōu)雅的停止一個線程
寫在開頭
經過上幾篇博文的學習,我們知道在Java中可以通過new Thread().start()創(chuàng)建一個線程,那今天我們就來思考另外一個問題:線程的終止
自然終止有兩種情況:
1. 線程的任務執(zhí)行完成;
2. 線程在執(zhí)行任務過程中發(fā)生異常。
start之后,如果線程沒有走到終止狀態(tài),我們該如何停止這個線程呢?
為什么stop終止不可用
翻看Thread源碼后,發(fā)現其提供過一個stop()方法,可以用來終止線程,我們看一下它的源碼。
【源碼解析1】
@Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW", it can't change to // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); }
這個方法使用了@Deprecated修飾,代表著它是廢棄的方法,在Java的編碼規(guī)約中,過時的方法不建議繼續(xù)使用,并且在這個方法的注釋中官方也提示說這是一個不安全的強制惡意中斷方法,會破壞線程的原子性
因此,在這里強烈建議大家不要再用stop方法去停止線程了!
如何優(yōu)雅的停止一個線程
我們知道線程只有從 runnable 狀態(tài)(可運行/運行狀態(tài)) 才能進入terminated 狀態(tài)(終止狀態(tài)),如果線程處于 blocked、waiting、timed_waiting 狀態(tài)(休眠狀態(tài)),就需要通過 Thread 類的 interrupt() 方法,讓線程從休眠狀態(tài)進入 runnable 狀態(tài),從而結束線程。
這里就涉及到了一個概念“線程中斷”,這是一種協作機制,當其他線程通知需要被中斷的線程后,線程中斷的狀態(tài)被設置為 true,但是具體被要求中斷的線程要怎么處理,完全由被中斷線程自己決定,可以在合適的時機中斷請求,也可以完全不處理繼續(xù)執(zhí)行下去,這樣一來,安全性就得到了保障。
Thread類中提供線程中斷的方法如下:
- Thread.interrupt():中斷線程。這里的中斷線程并不會立即停止線程,而是設置線程的中斷狀態(tài)為 true(默認是 flase);
- Thread.currentThread().isInterrupted():測試當前線程是否被中斷。線程的中斷狀態(tài)會受這個方法的影響,調用一次可以使線程中斷狀態(tài)變?yōu)?true,調用兩次會使這個線程的中斷狀態(tài)重新轉為 false;
- Thread.isInterrupted():測試當前線程是否被中斷。與上面方法不同的是調用這個方法并不會影響線程的中斷狀態(tài)。
Ok,寫了那么多,我們來寫一個小的demo測試一下線程中斷的方法。
【代碼示例】
public class Test { public static void main(String[] args) { //測試系統監(jiān)控器 testSystemMonitor(); } /** * 測試系統監(jiān)控器 */ public static void testSystemMonitor() { SystemMonitor sm = new SystemMonitor(); sm.start(); try { //運行 10 秒后停止監(jiān)控 Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("監(jiān)控任務啟動 10 秒后,停止..."); sm.stop(); } } /*系統監(jiān)控器*/ class SystemMonitor { private Thread t; //線程中斷標識 private volatile boolean stop = false; /** * 啟動一個線程監(jiān)控系統 */ void start() { t = new Thread(() -> { while (!stop) {//判斷當前線程是否被打斷 System.out.println("正在監(jiān)控系統..."); try { Thread.sleep(3 * 1000L);//執(zhí)行 3 秒 System.out.println("任務執(zhí)行 3 秒"); System.out.println("監(jiān)控的系統正常!"); } catch (InterruptedException e) { System.out.println("任務執(zhí)行被中斷..."); //重新設置線程為中斷狀態(tài),保證JVM拋異常情況下,中斷狀態(tài)仍為true。 Thread.currentThread().interrupt(); } } }); t.start(); } //線程中斷 void stop() { stop = true; t.interrupt(); } }
在這里我們先創(chuàng)建了一個SystemMonitor類作為系統檢測器,每3秒一循環(huán)的進行檢測,考慮到在Thread.currentThread().isInterrupted()可能在某些情況下中斷失效,所以我們這里自定義一個stop變量,作為線程中斷的標識,檢測線程啟動先對標識位進行判斷。
然后,我們在Test類中寫一個測試方法,調用這個系統監(jiān)控器,進行檢測,并設置10秒后,調用stop方法中斷檢測線程,將中斷標識stop設置為true。啟動代碼后,我們在控制臺可以看到這樣的輸出:
正在監(jiān)控系統...
任務執(zhí)行 3 秒
監(jiān)控的系統正常!
正在監(jiān)控系統...
任務執(zhí)行 3 秒
監(jiān)控的系統正常!
正在監(jiān)控系統...
任務執(zhí)行 3 秒
監(jiān)控的系統正常!
正在監(jiān)控系統...
監(jiān)控任務啟動 10 秒后,停止...
任務執(zhí)行被中斷...
與我們的預期一樣,監(jiān)控線程在執(zhí)行了3個循環(huán)的檢測任務后,被成功中斷調。到這里,我們就成功的、安全的、優(yōu)雅的停止了一個線程啦!
到此這篇關于淺析在Java中如何優(yōu)雅的停止一個線程的文章就介紹到這了,更多相關Java停止線程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring+SpringMVC+JDBC實現登錄的示例(附源碼)
這篇文章主要介紹了Spring+SpringMVC+JDBC實現登錄的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-05-05SpringMVC Mybatis配置多個數據源并切換代碼詳解
這篇文章主要介紹了SpringMVC Mybatis配置多個數據源并切換代碼詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11SparkStreaming-Kafka通過指定偏移量獲取數據實現
這篇文章主要為大家介紹了SparkStreaming-Kafka通過指定偏移量獲取數據,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06