線程生命週期控制是多線程編程核心難點。本文系統講解 start() vs run() 本質區別、interrupt() 中斷機制、阻塞態與運行態線程的安全終止模式、常見錯誤與最佳實踐。
| 方法 | 線程 | 執行上下文 | 可重複調用 |
|---|---|---|---|
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() 行為 | 中斷標誌 | 異常 |
|---|---|---|---|
| 運行態 | 設置中斷標誌為 true | true | 無 |
| 阻塞態 (sleep/wait/join) | 清除中斷標誌 → 拋出 InterruptedException | false (被清除) | InterruptedException |
| 阻塞態 (I/O/Selector) | 設置中斷標誌 → 通道關閉 → 返回/拋出異常 | true | ClosedByInterruptException / AsynchronousCloseException |
| 已終止 | 無效果 | false | 無 |
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 循環外**,異常直接跳出循環終止線程。
while (!isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// 處理異常但**未跳出循環**!
// 中斷標誌已被睡眠清除,isInterrupted() 為 false,循環繼續!
}
}
修正:在 catch 中 break 或 return,或將 try-catch 包裹整個 while。
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();
}
}
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
將 try-catch 移入 while 內 → 線程永不結束,狀態保持 RUNNABLE/TIMED_WAITING 交替。
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():強制釋放鎖 → 數據不一致;拋出 ThreadDeath → 可能不被捕獲suspend():持有鎖暫停 → 死險極高isInterrupted() 檢查 + InterruptedException 捕獲volatile boolean 標誌Thread.currentThread().interrupt()Future.cancel(true) 內部調用 interrupt()setDaemon(true),JVM 退出時自動終止線程生命週期控制核心在於:**start() 創建新線程,interrupt() 發送協作式中斷請求,運行態輪詢 isInterrupted(),阻塞態捕獲 InterruptedException,catch 在 while 外或顯式跳出,volatile 保證標誌可見性,finally 保證資源釋放**。拒絕 stop()/suspend(),擁抱協作式中斷,寫出健壯多線程代碼。