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

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

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

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

