Java 沒有提供直接停止線程的安全方法(stop() 已棄用),而是採用協作式中斷機制。本文系統講解中斷原理、API 語義、阻塞方法響應、異常處理模式及實戰場景,助你寫出可靠的併發代碼。
Thread 實例持有 boolean interrupted 標誌interrupt():將標誌置 trueisInterrupted():讀取標誌不清除Thread.interrupted():讀取當前線程標誌並清除中斷只是通知,「被中斷線程」自行決定何時、如何響應。若線程忽略標誌,中斷無效。
| 方法類別 | 代表方法 | 響應行為 |
|---|---|---|
| 可中斷阻塞 | Thread.sleep()Object.wait()Thread.join()BlockingQueue.take/put()Selector.select() | 拋出 InterruptedException,自動清除中斷標誌 |
| 不可中斷阻塞 | Socket.read()FileInputStream.read()synchronized 獲取鎖 | 不響應中斷,標誌保持,需關閉底層資源(如 socket.close())才能喚醒 |
| 可中斷鎖 | ReentrantLock.lockInterruptibly()tryLock(timeout) | 拋出 InterruptedException,清除標誌 |
public void processQueue(BlockingQueue<Task> queue) throws InterruptedException {
while (true) {
Task task = queue.take(); // 可中斷阻塞
execute(task);
}
}
// 調用者決定如何處理異常
public void runTask(Runnable task) {
try {
while (!Thread.currentThread().isInterrupted()) {
// 非阻塞邏輯,定期檢查
if (Thread.interrupted()) throw new InterruptedException(); // 主動檢查並清除
doWork();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 關鍵:恢復標誌供上層感知
}
}
public void shutdown() {
try {
blockingQueue.take();
} catch (InterruptedException e) {
// 明確知道此線程即將結束,無需傳播
log.info("線程被中斷,正常退出");
}
}
public class PrimeCalculator implements Runnable {
private final long limit;
private volatile boolean cancelled = false;
@Override
public void run() {
for (long i = 2; i <= limit && !cancelled; i++) {
if (isPrime(i)) foundPrimes.add(i);
// 每 1000 次檢查一次中斷,平衡性能與響應
if (i % 1000 == 0 && Thread.interrupted()) cancelled = true;
}
}
public void cancel() { cancelled = true; } // 也可調用 thread.interrupt()
}
public class GracefulService {
private final Thread worker = new Thread(this::loop);
private final ServerSocket serverSocket;
public void start() { worker.start(); }
public void stop() throws IOException {
worker.interrupt(); // 1. 發送中斷信號
serverSocket.close(); // 2. 關閉阻塞資源(accept() 會拋出 SocketException)
try { worker.join(5000); } // 3. 等待線程終結
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
private void loop() {
try {
while (!Thread.currentThread().isInterrupted()) {
Socket client = serverSocket.accept(); // 可被 close() 喚醒
executor.submit(() -> handle(client));
}
} catch (SocketException e) {
// 正常關閉路徑
} catch (IOException e) {
log.error("服務異常", e);
}
}
}
FutureTask<Result> future = new FutureTask<>(heavyComputation);
executor.submit(future);
// 5 秒後取消
executor.schedule(() -> future.cancel(true), 5, TimeUnit.SECONDS); // true = mayInterruptIfRunning
// 獲取結果
try {
Result r = future.get();
} catch (CancellationException e) {
// 任務被取消
}
| 維度 | Thread.stop()(棄用) | interrupt() |
|---|---|---|
| 機制 | 拋出 ThreadDeath (Error) | 設置標誌 / 拋出 InterruptedException |
| 鎖釋放 | 強制釋放所有鎖 → 數據不一致風險 | 不強制釋放,由代碼決定 |
| 安全性 | 極不安全,可能損壞對象不變量 | 安全,協作式 |
| 適用場景 | 無(嚴禁使用) | 所有需取消的場景 |
catch (InterruptedException e) { } → 必須恢復標誌或拋出isInterrupted() → 無法及時退出synchronized → 改用 NIO Channel、ReentrantLocklog.info("Thread interrupted")Java 線程中斷是「通知機制」而非「強制終止」。掌握 阻塞方法分類、三大處理模式、標誌傳播規則,配合 volatile 取消標誌、ReentrantLock 可中斷鎖、Future.cancel(true) 高級 API,即可構建健壯的可取消併發系統。