Java線程中斷的實現(xiàn)過程
線程的調(diào)度和執(zhí)行由操作系統(tǒng)決定,而 Java 通過提供一系列 API 來與操作系統(tǒng)交互,從而實現(xiàn)對線程的控制。
現(xiàn)代操作系統(tǒng)對線程的控制遵循兩個核心原則:
1、搶占式調(diào)度:操作系統(tǒng)可以強(qiáng)制剝奪線程的 CPU 時間片(如時間片輪轉(zhuǎn)調(diào)度)
2、協(xié)作式中斷:依賴線程主動響應(yīng)中斷信號(Java 的 interrupt())
因此Java 虛擬機(jī)(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,工作線程可以在運(yùn)行過程中檢查該變量的狀態(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("線程運(yùn)行中: " + 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();
// 線程運(yùn)行一段時間
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)制中止線程的方式,該方法會立即終止線程,而不執(zhí)行清理工作(如釋放資源、完成文件操作等),因此容易導(dǎo)致資源泄漏和線程安全問題,已被 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)行一段時間
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() 方法會被廢棄呢? JavaDoc 中Why is Thread.stop deprecated?這篇官方文檔解釋了其被廢棄的原因。
總結(jié)如下:
- 不可控的資源釋放:線程可能持有鎖或資源,強(qiáng)制終止可能導(dǎo)致資源泄漏。
- 數(shù)據(jù)不一致:線程可能在執(zhí)行關(guān)鍵操作時被中斷,導(dǎo)致數(shù)據(jù)損壞。
- 難以調(diào)試:強(qiáng)制終止可能引發(fā)不可預(yù)測的異常。
3、interrupt() 協(xié)作式中斷
因為Thread.stop()中斷線程的粗暴性,Java 官方已經(jīng)廢棄了該方法,那么官方還有沒有其它更優(yōu)雅的方案來中斷線程呢?答案是有的:那就是使用interrupt()協(xié)作式中斷。
什么是協(xié)作式中斷?
協(xié)作式中斷就是區(qū)別于 Thread.stop(),它是非強(qiáng)制且需要配合的。
非強(qiáng)制性:
interrupt() 方法不會強(qiáng)制終止線程,而是通過設(shè)置中斷標(biāo)志來通知線程應(yīng)該停止。線程可以選擇中斷,也可以忽略這個中斷請求,繼續(xù)執(zhí)行。
需要配合:
使用 interrupt() 方法,線程需要主動檢查中斷標(biāo)志,或者在阻塞方法中處理 InterruptedException,才能正常響應(yīng)中斷請求。這種機(jī)制是需要線程和中斷請求者之間相互“配合”的。
通過示例來演示下協(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秒
// 請求線程中斷
thread.interrupt();
System.out.println("主線程請求線程中斷");
}
}運(yùn)行結(jié)果: 線程運(yùn)行中: 1 線程運(yùn)行中: 2 線程運(yùn)行中: 3 線程運(yùn)行中: 4 線程運(yùn)行中: 5 主線程請求線程中斷 捕獲到中斷異常,但線程繼續(xù)運(yùn)行! 線程運(yùn)行中: 6 線程運(yùn)行中: 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("線程運(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秒
// 請求線程中斷
thread.interrupt();
System.out.println("主線程請求線程中斷");
}
}運(yùn)行結(jié)果: 線程運(yùn)行中: 1 線程運(yùn)行中: 2 線程運(yùn)行中: 3 線程運(yùn)行中: 4 線程運(yùn)行中: 5 主線程請求線程中斷 線程被中斷! 線程已優(yōu)雅停止
在這個示例中,我們在捕獲到異常后,正確的恢復(fù)了中斷標(biāo)志,線程優(yōu)雅地被中斷。
關(guān)于在線程運(yùn)行狀態(tài)會出現(xiàn)的方法,可參考如下:

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

thread.stop() 方法由于其強(qiáng)制性和不安全性,已被廢棄,不推薦使用。通過合理使用協(xié)作式中斷機(jī)制,可以實現(xiàn)線程的優(yōu)雅停止,提高程序的健壯性和可靠性。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Maven dependencies與dependencyManagement的區(qū)別詳解
這篇文章主要介紹了Maven dependencies與dependencyManagement的區(qū)別詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04
Java如何跳出當(dāng)前多重循環(huán)你知道嗎
這篇文章主要為大家介紹了Java跳出當(dāng)前多重循環(huán),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01
Spring?中使用?Validation?注解校驗參數(shù)的方法
本文介紹了如何在Spring中使用Validation注解進(jìn)行參數(shù)校驗,包括引入依賴、簡單示例、常見校驗注解分類與說明、分組校驗和自定義校驗,通過這些方法,可以方便地對Controller、Service等層面的參數(shù)進(jìn)行校驗,確保數(shù)據(jù)的合法性和一致性,感興趣的朋友跟隨小編一起看看吧2024-11-11
spring?retry實現(xiàn)方法請求重試的使用步驟
這篇文章主要介紹了spring?retry實現(xiàn)方法請求重試及使用步驟,本文分步驟通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07
Java動態(tài)替換properties文件中鍵值方式
這篇文章主要介紹了Java動態(tài)替換properties文件中鍵值方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
使用Java和Redis實現(xiàn)高效的短信防轟炸方案
在當(dāng)今互聯(lián)網(wǎng)應(yīng)用中,短信驗證碼已成為身份驗證的重要手段,然而,這也帶來了"短信轟炸"的安全風(fēng)險?-?惡意用戶利用程序自動化發(fā)送大量短信請求,導(dǎo)致用戶被騷擾和企業(yè)短信成本激增,本文將詳細(xì)介紹如何使用Java和Redis實現(xiàn)高效的短信防轟炸解決方案,需要的朋友可以參考下2025-04-04

