首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程基礎:interrupt() 機制與線程安全終止模式

     編輯:Java 相關知識

前言

正確終止線程是多線程編程的核心技能。Thread.stop()/suspend() 因不安全已棄用,現代 Java 依賴中斷機制(interrupt())配合協作式標記實現安全終止。本文深度解析 interrupt() 語義、阻塞/運行態終止模式、InterruptedException 處理陷阱,以及 interrupted() 與 isInterrupted() 的區別。

一、interrupt() 官方語義解讀

  • 作用:請求中斷目標線程,不強制終止。
  • 權限檢查:非自我中斷時調用 checkAccess(),可能拋 SecurityException。
  • 阻塞態響應:線程在 wait()/sleep()/join() 等阻塞方法中收到中斷 → 清除中斷標記 + 拋出 InterruptedException。
  • Selector 阻塞:設置中斷標記 + 立即從選擇操作返回。
  • 運行態響應:僅設置中斷標記 = true,線程繼續執行,需自行輪詢 isInterrupted()。
  • 已終止線程:中斷無效果。

二、兩大狀態下的終止模式

1. 阻塞態終止:捕獲 InterruptedException

// ✅ 正確: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
    }
}

2. 運行態終止:輪詢中斷標記

// 方式一:使用線程自帶中斷標記
@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 優化導致讀到舊值。

3. 通用模板:兼容阻塞與運行態

@Override public void run() {
    try {
        while (!Thread.currentThread().isInterrupted()) { // 運行態檢查
            // 執行任務(可能含阻塞調用)
        }
    } catch (InterruptedException e) { // 阻塞態拋出異常
        // 線程終止
        Thread.currentThread().interrupt(); // 可選:恢復標記
    }
}

三、實戰演示:interrupt() 終止睡眠線程

Demo1:catch 在 while 外(正確終止)

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.

Demo2:catch 在 while 內(死循環陷阱)


// 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,或改用通用模板。

Demo3:自定義 volatile 標記終止運行態

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,運行線程下次循環檢測到並退出。

四、interrupted() vs isInterrupted()

方法屬於功能副作用
Thread.interrupted()靜態方法檢測當前線程中斷標記清除標記(設為 false)
thread.isInterrupted()實例方法檢測指定線程中斷標記不清除標記

使用場景:

  • Thread.interrupted():當前線程自我檢查並消費中斷信號(如庫代碼內部)。
  • isInterrupted():外部監控線程狀態、終止邏輯判斷(保留標記供後續判斷)。

五、總結:線程終止最佳實踐清單

  1. 永不使用 stop()/suspend()/resume()。
  2. 阻塞調用(sleep/wait/join/IO)依賴 InterruptedException 終止,catch 放在循環外。
  3. 純計算/非阻塞循環依賴 isInterrupted() 或 volatile boolean 標記輪詢。
  4. 通用模板:while (!isInterrupted()) 外層 catch InterruptedException。
  5. 捕獲 InterruptedException 後,視業務決定是 Thread.currentThread().interrupt() 恢復標記(傳遞給調用棧)還是直接結束。
  6. 提供 shutdown() 方法封裝 interrupt() + join() 等待終止,對外隱藏線程控制細節。
  7. 區分 interrupted()(消費標記)與 isInterrupted()(查看標記),避免誤清除導致邏輯錯誤。

掌握中斷機制的語義邊界,是編寫健壯、可優雅關閉的多線程程序的前提。下次寫 while(true) 循環時,請先問自己:如何讓它停下來?

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved