正確終止線程是多線程編程的核心技能。Thread.stop()/suspend() 因不安全已棄用,現代 Java 依賴中斷機制(interrupt())配合協作式標記實現安全終止。本文深度解析 interrupt() 語義、阻塞/運行態終止模式、InterruptedException 處理陷阱,以及 interrupted() 與 isInterrupted() 的區別。
checkAccess(),可能拋 SecurityException。wait()/sleep()/join() 等阻塞方法中收到中斷 → 清除中斷標記 + 拋出 InterruptedException。isInterrupted()。// ✅ 正確:catch 在 while 外,異常直接跳出循環
@Override public void run() {
try {
while (true) { /* 執行任務,含 sleep/wait/join */ }
} catch (InterruptedException e) {
// 線程終止,可選:Thread.currentThread().interrupt() 恢復標記供上層感知
}
}
// ❌ 錯誤:catch 在 while 內,需手動 break,易漏寫導致死循環
@Override public void run() {
while (true) {
try { /* 任務 */ }
catch (InterruptedException e) { break; } // 必須 break
}
}
// 方式一:使用線程自帶中斷標記
@Override public void run() {
while (!Thread.currentThread().isInterrupted()) { // 檢查標記
// 執行任務(無阻塞調用)
}
}
// 方式二:自定義 volatile 標記(更靈活,可封裝 stop() 方法)
private volatile boolean running = true;
public void stopTask() { running = false; }
@Override public void run() {
while (running) { /* 任務 */ }
}
volatile 關鍵:保證 stopTask() 修改對運行線程可見,防止 JIT 優化導致讀到舊值。
@Override public void run() {
try {
while (!Thread.currentThread().isInterrupted()) { // 運行態檢查
// 執行任務(可能含阻塞調用)
}
} catch (InterruptedException e) { // 阻塞態拋出異常
// 線程終止
Thread.currentThread().interrupt(); // 可選:恢復標記
}
}
class MyThread extends Thread {
@Override public void run() {
try {
int i = 0;
while (!isInterrupted()) {
sleep(100);
System.out.println(getName() + " (" + getState() + ") loop " + ++i);
}
} catch (InterruptedException e) {
System.out.println(getName() + " (" + getState() + ") catch InterruptedException.");
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new MyThread("t1");
t1.start(); sleep(300); t1.interrupt(); sleep(300);
System.out.println(t1.getName() + " (" + t1.getState() + ") now.");
}
}
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) catch InterruptedException. t1 (TERMINATED) now.
// run() 方法 catch 移到 while 內
while (!isInterrupted()) {
try { sleep(100); }
catch (InterruptedException ie) { System.out.println("catch..."); }
System.out.println("loop " + ++i);
}
... catch InterruptedException. t1 (RUNNABLE) loop 3 t1 (RUNNABLE) loop 4 ... 死循環!
原因:阻塞態中斷 → 清除標記 + 拋異常 → isInterrupted() 變 false → while 繼續 → 死循環。
修正:catch 內 break 或 return,或改用通用模板。
class MyThread extends Thread {
private volatile boolean flag = true;
public void stopTask() { flag = false; }
@Override public void run() {
synchronized(this) {
try { while (flag) { sleep(100); System.out.println("loop"); } }
catch (InterruptedException e) {}
}
}
}
t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) interrupted. t1 (RUNNABLE) loop 3 t1 (TERMINATED) now.
主線程調用 stopTask() 修改 flag=false,運行線程下次循環檢測到並退出。
| 方法 | 屬於 | 功能 | 副作用 |
|---|---|---|---|
Thread.interrupted() | 靜態方法 | 檢測當前線程中斷標記 | 清除標記(設為 false) |
thread.isInterrupted() | 實例方法 | 檢測指定線程中斷標記 | 不清除標記 |
使用場景:
Thread.interrupted():當前線程自我檢查並消費中斷信號(如庫代碼內部)。isInterrupted():外部監控線程狀態、終止邏輯判斷(保留標記供後續判斷)。stop()/suspend()/resume()。sleep/wait/join/IO)依賴 InterruptedException 終止,catch 放在循環外。isInterrupted() 或 volatile boolean 標記輪詢。while (!isInterrupted()) 外層 catch InterruptedException。InterruptedException 後,視業務決定是 Thread.currentThread().interrupt() 恢復標記(傳遞給調用棧)還是直接結束。shutdown() 方法封裝 interrupt() + join() 等待終止,對外隱藏線程控制細節。interrupted()(消費標記)與 isInterrupted()(查看標記),避免誤清除導致邏輯錯誤。掌握中斷機制的語義邊界,是編寫健壯、可優雅關閉的多線程程序的前提。下次寫 while(true) 循環時,請先問自己:如何讓它停下來?