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

