Java線程中斷的實(shí)現(xiàn)過(guò)程
線程的調(diào)度和執(zhí)行由操作系統(tǒng)決定,而 Java 通過(guò)提供一系列 API 來(lái)與操作系統(tǒng)交互,從而實(shí)現(xiàn)對(duì)線程的控制。
現(xiàn)代操作系統(tǒng)對(duì)線程的控制遵循兩個(gè)核心原則:
1、搶占式調(diào)度:操作系統(tǒng)可以強(qiáng)制剝奪線程的 CPU 時(shí)間片(如時(shí)間片輪轉(zhuǎn)調(diào)度)
2、協(xié)作式中斷:依賴(lài)線程主動(dòng)響應(yīng)中斷信號(hào)(Java 的 interrupt())
因此Java 虛擬機(jī)(JVM)作為用戶態(tài)程序,其實(shí)是無(wú)法直接終止一個(gè)線程的。當(dāng)我們調(diào)用 thread.stop() 這類(lèi)方法時(shí),實(shí)際上是在用戶態(tài)層面發(fā)出中斷請(qǐng)求,而真正的線程終止還需要操作系統(tǒng)內(nèi)核的配合。
如下圖所示:

Java 線程中斷的三種實(shí)現(xiàn)方式:
1、volatile 標(biāo)志位:通過(guò)設(shè)置標(biāo)志位,使線程正常退出,能夠確保線程根據(jù)業(yè)務(wù)邏輯安全停止。
2、Thread.stop() 方法:已廢棄,不推薦使用,因?yàn)闀?huì)導(dǎo)致線程安全問(wèn)題和資源泄漏。
3、Thread.interrupt() 方法:推薦使用,通過(guò)設(shè)置中斷標(biāo)志來(lái)提示線程停止,能夠優(yōu)雅的停止線程。
1、volatile 標(biāo)志位
volatile關(guān)鍵字可以確保變量的修改對(duì)所有線程立即可見(jiàn)。
通過(guò)在共享變量上使用volatile,工作線程可以在運(yùn)行過(guò)程中檢查該變量的狀態(tài),一旦檢測(cè)到變化即可清理資源并結(jié)束執(zhí)行。
public class WorkerThread extends Thread {
privatevolatileboolean running = true;
public void run() {
int i = 1;
while (running) { // 檢查標(biāo)志位
System.out.println("線程運(yùn)行中: " + i++);
try {
Thread.sleep(1000L); // 模擬業(yè)務(wù)操作
} catch (InterruptedException e) {
// 重新設(shè)置中斷標(biāo)識(shí)
Thread.currentThread().interrupt();
}
}
System.out.println("線程已優(yōu)雅停止");
}
public void cancel() {
running = false;
}
public static void main(String[] args) throws InterruptedException {
WorkerThread thread = new WorkerThread();
thread.start();
// 線程運(yùn)行一段時(shí)間
Thread.sleep(5000L);
// 修改標(biāo)志位,中斷線程
thread.cancel();
}
}輸出:
運(yùn)行結(jié)果: 線程運(yùn)行中: 1 線程運(yùn)行中: 2 線程運(yùn)行中: 3 線程運(yùn)行中: 4 線程運(yùn)行中: 5 線程已優(yōu)雅停止
2、stop()強(qiáng)制終止
Thread.stop()方法是 Java 早期提供的一種強(qiáng)制中止線程的方式,該方法會(huì)立即終止線程,而不執(zhí)行清理工作(如釋放資源、完成文件操作等),因此容易導(dǎo)致資源泄漏和線程安全問(wèn)題,已被 Java 官方棄用。
public class WorkerThread extends Thread {
public void run() {
int i = 1;
while (true) {
System.out.println("線程運(yùn)行中: " + i++);
try {
Thread.sleep(1000L); // 模擬業(yè)務(wù)操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) throws InterruptedException {
WorkerThread thread = new WorkerThread();
thread.start();
// 線程運(yùn)行一段時(shí)間
Thread.sleep(5000L);
// 調(diào)用 stop 方法,強(qiáng)制終止線程
thread.stop();
System.out.println("線程已強(qiáng)制終止");
}
}運(yùn)行結(jié)果: 線程運(yùn)行中: 1 線程運(yùn)行中: 2 線程運(yùn)行中: 3 線程運(yùn)行中: 4 線程運(yùn)行中: 5 線程已強(qiáng)制終止
為什么 stop() 方法會(huì)被廢棄呢? JavaDoc 中Why is Thread.stop deprecated?這篇官方文檔解釋了其被廢棄的原因。
總結(jié)如下:
- 不可控的資源釋放:線程可能持有鎖或資源,強(qiáng)制終止可能導(dǎo)致資源泄漏。
- 數(shù)據(jù)不一致:線程可能在執(zhí)行關(guān)鍵操作時(shí)被中斷,導(dǎo)致數(shù)據(jù)損壞。
- 難以調(diào)試:強(qiáng)制終止可能引發(fā)不可預(yù)測(cè)的異常。
3、interrupt() 協(xié)作式中斷
因?yàn)門(mén)hread.stop()中斷線程的粗暴性,Java 官方已經(jīng)廢棄了該方法,那么官方還有沒(méi)有其它更優(yōu)雅的方案來(lái)中斷線程呢?答案是有的:那就是使用interrupt()協(xié)作式中斷。
什么是協(xié)作式中斷?
協(xié)作式中斷就是區(qū)別于 Thread.stop(),它是非強(qiáng)制且需要配合的。
非強(qiáng)制性:
interrupt() 方法不會(huì)強(qiáng)制終止線程,而是通過(guò)設(shè)置中斷標(biāo)志來(lái)通知線程應(yīng)該停止。線程可以選擇中斷,也可以忽略這個(gè)中斷請(qǐng)求,繼續(xù)執(zhí)行。
需要配合:
使用 interrupt() 方法,線程需要主動(dòng)檢查中斷標(biāo)志,或者在阻塞方法中處理 InterruptedException,才能正常響應(yīng)中斷請(qǐng)求。這種機(jī)制是需要線程和中斷請(qǐng)求者之間相互“配合”的。
通過(guò)示例來(lái)演示下協(xié)作式中斷:
public class WorkerThread extends Thread {
@Override
public void run() {
int i = 1;
while (!Thread.currentThread().isInterrupted()) { // 顯式檢查中斷狀態(tài)
System.out.println("線程運(yùn)行中: " + i++);
try {
Thread.sleep(1000L); // 模擬業(yè)務(wù)操作
} catch (InterruptedException e) {
System.out.println("捕獲到中斷異常,但線程繼續(xù)運(yùn)行!");
// Thread.currentThread().interrupt();
}
}
System.out.println("線程已優(yōu)雅停止");
}
public static void main(String[] args) throws InterruptedException {
WorkerThread thread = new WorkerThread();
thread.start();
Thread.sleep(5000L); // 主線程等待5秒
// 請(qǐng)求線程中斷
thread.interrupt();
System.out.println("主線程請(qǐng)求線程中斷");
}
}運(yùn)行結(jié)果: 線程運(yùn)行中: 1 線程運(yùn)行中: 2 線程運(yùn)行中: 3 線程運(yùn)行中: 4 線程運(yùn)行中: 5 主線程請(qǐng)求線程中斷 捕獲到中斷異常,但線程繼續(xù)運(yùn)行! 線程運(yùn)行中: 6 線程運(yùn)行中: 7
可以看到,main() 方法中已經(jīng)請(qǐng)求了線程中斷,但是線程并沒(méi)有被中斷,還是繼續(xù)執(zhí)行下去了,為什么?
interrupt() 方法被調(diào)用時(shí),線程的中斷標(biāo)志會(huì)被設(shè)置為 true。它的調(diào)用只是傳遞了一個(gè)中斷信號(hào),線程本身不會(huì)立即停止執(zhí)行。
它需要通過(guò)檢查中斷標(biāo)志(如使用 Thread.currentThread().isInterrupted())來(lái)決定是否停止當(dāng)線程。
并且當(dāng)線程處于以下阻塞方法(如 sleep()、wait()、join())時(shí),
JVM 會(huì)做兩件事:1.拋出 InterruptedException 異常;2.清除中斷標(biāo)志,重新設(shè)置為 false。
那么,當(dāng)我們?cè)诔绦蛑胁东@到 InterruptedException 異常時(shí),我們需要做出明確的選擇:
處理異常并終止線程的執(zhí)行;重新設(shè)置中斷標(biāo)識(shí)讓上層邏輯感知到。
上邊的示例中,我們?cè)诓东@了 InterruptedException 異常后,這時(shí)中斷標(biāo)志已經(jīng)被重置,而程序中卻沒(méi)有做任何的處理,導(dǎo)致了在下一輪的循環(huán)判斷中,檢測(cè)的中斷狀態(tài)不對(duì),所以線程沒(méi)有按預(yù)期執(zhí)行中斷。
正例
public class WorkerThread extends Thread {
@Override
public void run() {
int i = 1;
while (!Thread.currentThread().isInterrupted()) { // 顯式檢查中斷狀態(tài)
System.out.println("線程運(yùn)行中: " + i++);
try {
Thread.sleep(1000L); // 模擬業(yè)務(wù)操作
} catch (InterruptedException e) {
// 重新設(shè)置中斷標(biāo)志,通知上層邏輯進(jìn)行中斷處理
System.out.println("線程被中斷!");
Thread.currentThread().interrupt();
}
}
System.out.println("線程已優(yōu)雅停止");
}
public static void main(String[] args) throws InterruptedException {
WorkerThread thread = new WorkerThread();
thread.start();
Thread.sleep(5000L); // 主線程等待5秒
// 請(qǐng)求線程中斷
thread.interrupt();
System.out.println("主線程請(qǐng)求線程中斷");
}
}運(yùn)行結(jié)果: 線程運(yùn)行中: 1 線程運(yùn)行中: 2 線程運(yùn)行中: 3 線程運(yùn)行中: 4 線程運(yùn)行中: 5 主線程請(qǐng)求線程中斷 線程被中斷! 線程已優(yōu)雅停止
在這個(gè)示例中,我們?cè)诓东@到異常后,正確的恢復(fù)了中斷標(biāo)志,線程優(yōu)雅地被中斷。
關(guān)于在線程運(yùn)行狀態(tài)會(huì)出現(xiàn)的方法,可參考如下:

總結(jié)
在實(shí)際開(kāi)發(fā)中,推薦使用 volatile 標(biāo)志位和 thread.interrupt() 協(xié)作式中斷來(lái)中止線程。這兩種方式能夠確保線程的安全和資源的正確釋放,避免線程不安全和資源泄漏問(wèn)題。
如下圖所示:

thread.stop() 方法由于其強(qiáng)制性和不安全性,已被廢棄,不推薦使用。通過(guò)合理使用協(xié)作式中斷機(jī)制,可以實(shí)現(xiàn)線程的優(yōu)雅停止,提高程序的健壯性和可靠性。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringData JPA審計(jì)功能(@CreatedDate與@LastModifiedDate)實(shí)現(xiàn)
Spring Data JPA的審計(jì)功能提供了一種強(qiáng)大而靈活的機(jī)制,用于自動(dòng)跟蹤實(shí)體的創(chuàng)建和修改信息,通過(guò)使用@CreatedDate和@LastModifiedDate注解,開(kāi)發(fā)者可以輕松地實(shí)現(xiàn)時(shí)間審計(jì),感興趣的可以了解一下2025-04-04
關(guān)于解決雪花算法生成的ID傳輸前端后精度丟失問(wèn)題
這篇文章主要介紹了關(guān)于解決雪花算法生成的ID傳輸前端后精度丟失問(wèn)題,雪花算法生成的ID傳輸?shù)角岸藭r(shí),會(huì)出現(xiàn)后三位精度丟失,本文提供了解決思路,需要的朋友可以參考下2023-03-03
Java的ThreadPoolExecutor業(yè)務(wù)線程池詳細(xì)解析
這篇文章主要介紹了Java線程池ThreadPoolExecutor詳細(xì)解析,任務(wù)剛開(kāi)始進(jìn)來(lái)的時(shí)候就創(chuàng)建核心線程,核心線程滿了會(huì)把任務(wù)放到阻塞隊(duì)列,阻塞隊(duì)列滿了之后才會(huì)創(chuàng)建空閑線程,達(dá)到最大線程數(shù)之后,再有任務(wù)進(jìn)來(lái),就只能執(zhí)行拒絕策略了,需要的朋友可以參考下2024-01-01
Spring Security基于散列加密方案實(shí)現(xiàn)自動(dòng)登錄功能
為了提高項(xiàng)目的用戶體驗(yàn),我們可以在項(xiàng)目中添加自動(dòng)登錄功能,當(dāng)然也要給用戶提供退出登錄的功能。接下來(lái)學(xué)習(xí)下Spring Security基于散列加密方案實(shí)現(xiàn)自動(dòng)登錄功能,一起看看吧2021-09-09
java集合模擬實(shí)現(xiàn)斗地主洗牌和發(fā)牌
這篇文章主要為大家詳細(xì)介紹了java集合模擬實(shí)現(xiàn)斗地主洗牌和發(fā)牌,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
RestTemplate添加HTTPS證書(shū)全過(guò)程解析
這篇文章主要介紹了RestTemplate添加HTTPS證書(shū)全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
@Schedule?如何解決定時(shí)任務(wù)推遲執(zhí)行
這篇文章主要介紹了@Schedule?如何解決定時(shí)任務(wù)推遲執(zhí)行問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
Spring定時(shí)任務(wù)@Scheduled注解(cron表達(dá)式fixedRate?fixedDelay)
這篇文章主要為大家介紹了Spring定時(shí)任務(wù)@Scheduled注解(cron表達(dá)式fixedRate?fixedDelay)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
feign name指定服務(wù)調(diào)用無(wú)效問(wèn)題及解決
文章主要介紹了FeignClient注解的常用屬性,并通過(guò)一個(gè)具體的例子說(shuō)明了為什么某個(gè)Feign調(diào)用需要使用url指定路徑才能訪問(wèn),最后,文章給出了解決辦法,即使用path屬性指定前綴2024-11-11

