淺析在Java中如何優(yōu)雅的停止一個(gè)線程
寫在開頭
經(jīng)過上幾篇博文的學(xué)習(xí),我們知道在Java中可以通過new Thread().start()創(chuàng)建一個(gè)線程,那今天我們就來思考另外一個(gè)問題:線程的終止
自然終止有兩種情況:
1. 線程的任務(wù)執(zhí)行完成;
2. 線程在執(zhí)行任務(wù)過程中發(fā)生異常。
start之后,如果線程沒有走到終止?fàn)顟B(tài),我們該如何停止這個(gè)線程呢?
為什么stop終止不可用
翻看Thread源碼后,發(fā)現(xiàn)其提供過一個(gè)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()); }
這個(gè)方法使用了@Deprecated修飾,代表著它是廢棄的方法,在Java的編碼規(guī)約中,過時(shí)的方法不建議繼續(xù)使用,并且在這個(gè)方法的注釋中官方也提示說這是一個(gè)不安全的強(qiáng)制惡意中斷方法,會破壞線程的原子性
因此,在這里強(qiáng)烈建議大家不要再用stop方法去停止線程了!
如何優(yōu)雅的停止一個(gè)線程
我們知道線程只有從 runnable 狀態(tài)(可運(yùn)行/運(yùn)行狀態(tài)) 才能進(jìn)入terminated 狀態(tài)(終止?fàn)顟B(tài)),如果線程處于 blocked、waiting、timed_waiting 狀態(tài)(休眠狀態(tài)),就需要通過 Thread 類的 interrupt() 方法,讓線程從休眠狀態(tài)進(jìn)入 runnable 狀態(tài),從而結(jié)束線程。
這里就涉及到了一個(gè)概念“線程中斷”,這是一種協(xié)作機(jī)制,當(dāng)其他線程通知需要被中斷的線程后,線程中斷的狀態(tài)被設(shè)置為 true,但是具體被要求中斷的線程要怎么處理,完全由被中斷線程自己決定,可以在合適的時(shí)機(jī)中斷請求,也可以完全不處理繼續(xù)執(zhí)行下去,這樣一來,安全性就得到了保障。
Thread類中提供線程中斷的方法如下:
- Thread.interrupt():中斷線程。這里的中斷線程并不會立即停止線程,而是設(shè)置線程的中斷狀態(tài)為 true(默認(rèn)是 flase);
- Thread.currentThread().isInterrupted():測試當(dāng)前線程是否被中斷。線程的中斷狀態(tài)會受這個(gè)方法的影響,調(diào)用一次可以使線程中斷狀態(tài)變?yōu)?true,調(diào)用兩次會使這個(gè)線程的中斷狀態(tài)重新轉(zhuǎn)為 false;
- Thread.isInterrupted():測試當(dāng)前線程是否被中斷。與上面方法不同的是調(diào)用這個(gè)方法并不會影響線程的中斷狀態(tài)。
Ok,寫了那么多,我們來寫一個(gè)小的demo測試一下線程中斷的方法。
【代碼示例】
public class Test { public static void main(String[] args) { //測試系統(tǒng)監(jiān)控器 testSystemMonitor(); } /** * 測試系統(tǒng)監(jiān)控器 */ public static void testSystemMonitor() { SystemMonitor sm = new SystemMonitor(); sm.start(); try { //運(yùn)行 10 秒后停止監(jiān)控 Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("監(jiān)控任務(wù)啟動 10 秒后,停止..."); sm.stop(); } } /*系統(tǒng)監(jiān)控器*/ class SystemMonitor { private Thread t; //線程中斷標(biāo)識 private volatile boolean stop = false; /** * 啟動一個(gè)線程監(jiān)控系統(tǒng) */ void start() { t = new Thread(() -> { while (!stop) {//判斷當(dāng)前線程是否被打斷 System.out.println("正在監(jiān)控系統(tǒng)..."); try { Thread.sleep(3 * 1000L);//執(zhí)行 3 秒 System.out.println("任務(wù)執(zhí)行 3 秒"); System.out.println("監(jiān)控的系統(tǒng)正常!"); } catch (InterruptedException e) { System.out.println("任務(wù)執(zhí)行被中斷..."); //重新設(shè)置線程為中斷狀態(tài),保證JVM拋異常情況下,中斷狀態(tài)仍為true。 Thread.currentThread().interrupt(); } } }); t.start(); } //線程中斷 void stop() { stop = true; t.interrupt(); } }
在這里我們先創(chuàng)建了一個(gè)SystemMonitor類作為系統(tǒng)檢測器,每3秒一循環(huán)的進(jìn)行檢測,考慮到在Thread.currentThread().isInterrupted()可能在某些情況下中斷失效,所以我們這里自定義一個(gè)stop變量,作為線程中斷的標(biāo)識,檢測線程啟動先對標(biāo)識位進(jìn)行判斷。
然后,我們在Test類中寫一個(gè)測試方法,調(diào)用這個(gè)系統(tǒng)監(jiān)控器,進(jìn)行檢測,并設(shè)置10秒后,調(diào)用stop方法中斷檢測線程,將中斷標(biāo)識stop設(shè)置為true。啟動代碼后,我們在控制臺可以看到這樣的輸出:
正在監(jiān)控系統(tǒng)...
任務(wù)執(zhí)行 3 秒
監(jiān)控的系統(tǒng)正常!
正在監(jiān)控系統(tǒng)...
任務(wù)執(zhí)行 3 秒
監(jiān)控的系統(tǒng)正常!
正在監(jiān)控系統(tǒng)...
任務(wù)執(zhí)行 3 秒
監(jiān)控的系統(tǒng)正常!
正在監(jiān)控系統(tǒng)...
監(jiān)控任務(wù)啟動 10 秒后,停止...
任務(wù)執(zhí)行被中斷...
與我們的預(yù)期一樣,監(jiān)控線程在執(zhí)行了3個(gè)循環(huán)的檢測任務(wù)后,被成功中斷調(diào)。到這里,我們就成功的、安全的、優(yōu)雅的停止了一個(gè)線程啦!
到此這篇關(guān)于淺析在Java中如何優(yōu)雅的停止一個(gè)線程的文章就介紹到這了,更多相關(guān)Java停止線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot 統(tǒng)一異常處理的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot 統(tǒng)一異常處理的實(shí)現(xiàn)示例,目的就是在異常發(fā)生時(shí),盡可能地減少破壞,下面就來介紹一下,感興趣的可以了解一下2024-07-07如何基于java向mysql數(shù)據(jù)庫中存取圖片
這篇文章主要介紹了如何基于java向mysql數(shù)據(jù)庫中存取圖片,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02Java程序運(yùn)行時(shí)出現(xiàn)亂碼問題的排查與解決方法
本文主要介紹了Java程序運(yùn)行時(shí)出現(xiàn)亂碼問題的排查與解決方法,包括檢查Java源文件編碼、檢查編譯時(shí)的編碼設(shè)置、檢查運(yùn)行時(shí)的編碼設(shè)置、檢查命令提示符的代碼頁、檢查命令提示符的字體、檢查 Java 程序的輸出代碼以及檢查環(huán)境變量,需要的朋友可以參考下2025-03-03mybatis-plus添加數(shù)據(jù)時(shí)id自增問題及解決
這篇文章主要介紹了mybatis-plus添加數(shù)據(jù)時(shí)id自增問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01JAVA設(shè)計(jì)模式之訪問者模式原理與用法詳解
這篇文章主要介紹了JAVA設(shè)計(jì)模式之訪問者模式,簡單說明了訪問者模式的原理,并結(jié)合實(shí)例分析了java訪問者模式的定義與用法,需要的朋友可以參考下2017-08-08