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

Java 多線程:線程啟動、中斷、終止完整指南 (start vs run, interrupt, 安全停止模式)

     編輯:Java 相關知識

前言

線程生命週期控制是多線程編程核心難點。本文系統講解 start() vs run() 本質區別、interrupt() 中斷機制、阻塞態與運行態線程的安全終止模式、常見錯誤與最佳實踐。

線程啟動:start() vs run()

本質區別

方法線程執行上下文可重複調用
start()**新線程**新線程棧❌ (IllegalThreadStateException)
run()**當前線程** (主線程)當前線程棧✅ 普通方法

代碼驗證

class MyThread extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

public class StartVsRun {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        System.out.println(Thread.currentThread().getName() + " call t.run()");
        t.run();      // 輸出: main is running
        System.out.println(Thread.currentThread().getName() + " call t.start()");
        t.start();    // 輸出: Thread-0 is running
    }
}

結論:start() 創建新線程並異步執行 run();直接調用 run() 只是同步調用普通方法,**不具備多線程特性**。

線程中斷:interrupt() 機制詳解

三種場景行為

線程狀態interrupt() 行為中斷標誌異常
運行態設置中斷標誌為 truetrue無
阻塞態 (sleep/wait/join)清除中斷標誌 → 拋出 InterruptedExceptionfalse (被清除)InterruptedException
阻塞態 (I/O/Selector)設置中斷標誌 → 通道關閉 → 返回/拋出異常trueClosedByInterruptException / AsynchronousCloseException
已終止無效果false無

關鍵 API

  • thread.interrupt():發送中斷請求
  • thread.isInterrupted():檢查中斷標誌,**不清除**
  • Thread.interrupted():檢查當前線程中斷標誌,**並清除** (靜態方法)

線程安全終止:兩大核心模式

模式一:中斷標誌 + 異常捕獲 (適用阻塞態)

@Override
public void run() {
    try {
        while (!Thread.currentThread().isInterrupted()) { // 1. 運行態檢查標誌
            // 執行任務...
            Thread.sleep(100); // 2. 阻塞態:睡眠中被 interrupt → 拋出異常
        }
    } catch (InterruptedException e) {
        // 3. 異常捕獲處:線程安全退出
        Thread.currentThread().interrupt(); // 重新設置標誌 (最佳實踐)
    }
}

關鍵點:catch 塊**在 while 循環外**,異常直接跳出循環終止線程。

錯誤示範:catch 在循環內 → 死循環

while (!isInterrupted()) {
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // 處理異常但**未跳出循環**!
        // 中斷標誌已被睡眠清除,isInterrupted() 為 false,循環繼續!
    }
}

修正:在 catch 中 break 或 return,或將 try-catch 包裹整個 while。

模式二:自定義 volatile 標誌 (適用運行態/無阻塞)

private volatile boolean running = true; // volatile 保證可見性

public void stopTask() { running = false; }

@Override
public void run() {
    while (running) { // 運行態輪詢標誌
        // 執行任務...
        // 若有阻塞調用,需結合 interrupt
    }
}

volatile 作用:保證主線程修改 running=false 立即對工作線程可見,防止 JIT 優化導致讀取舊值。

模式三:通用模板 (同時處理運行態+阻塞態)

@Override
public void run() {
    try {
        while (!Thread.currentThread().isInterrupted()) { // 運行態檢查
            // 執行任務...
            Thread.sleep(100); // 可能阻塞
        }
    } catch (InterruptedException e) {
        // 阻塞態被中斷 → 異常跳出循環
    } finally {
        // 清理資源:關閉流、釋放鎖、還原中斷標誌
        Thread.currentThread().interrupt();
    }
}

完整實戰演示

案例 1:中斷睡眠線程 (正確終止)

class TaskThread extends Thread {
    public void run() {
        try {
            while (!isInterrupted()) {
                Thread.sleep(100);
                System.out.println(getName() + " loop");
            }
        } catch (InterruptedException e) {
            System.out.println(getName() + " caught InterruptedException, exiting");
        }
    }
}

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        TaskThread t = new TaskThread();
        t.start();
        Thread.sleep(300); // 主線程等待
        t.interrupt();     // 發送中斷
        Thread.sleep(300);
        System.out.println(t.getName() + " state: " + t.getState()); // TERMINATED
    }
}

輸出:循環 2 次 → 主線程 interrupt → 睡眠中拋異常 → catch 處理 → 線程 TERMINATED

案例 2:catch 在循環內導致死循環 (錯誤)

將 try-catch 移入 while 內 → 線程永不結束,狀態保持 RUNNABLE/TIMED_WAITING 交替。

案例 3:volatile 標誌終止運行態線程

class VolatileThread extends Thread {
    private volatile boolean running = true;
    public void stopTask() { running = false; }
    public void run() {
        while (running) {
            // 無阻塞任務
        }
        System.out.println(getName() + " stopped");
    }
}

禁用方法:stop() / suspend() / resume()

  • stop():強制釋放鎖 → 數據不一致;拋出 ThreadDeath → 可能不被捕獲
  • suspend():持有鎖暫停 → 死險極高
  • JDK 明確標註 @Deprecated,嚴禁使用

最佳實踐總結

  1. 啟動必用 start(),切勿直接調用 run()
  2. 終止首選 interrupt(),配合 isInterrupted() 檢查 + InterruptedException 捕獲
  3. catch 放在 while 外層,或 catch 內显式 break/return
  4. 無阻塞運行態:用 volatile boolean 標誌
  5. finally 塊清理資源,重新設置中斷標誌 Thread.currentThread().interrupt()
  6. 線程池任務:Future.cancel(true) 內部調用 interrupt()
  7. 守護線程:setDaemon(true),JVM 退出時自動終止

總結

線程生命週期控制核心在於:**start() 創建新線程,interrupt() 發送協作式中斷請求,運行態輪詢 isInterrupted(),阻塞態捕獲 InterruptedException,catch 在 while 外或顯式跳出,volatile 保證標誌可見性,finally 保證資源釋放**。拒絕 stop()/suspend(),擁抱協作式中斷,寫出健壯多線程代碼。

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