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

Java 線程安全中斷指南:正確停止運行中線程的三種模式與最佳實踐

     編輯:Java 相關知識

直接呼叫 Thread.stop() 已被棄用(不安全、可能損壞鎖狀態),Thread.interrupt() 僅能喚醒阻塞操作。正確停止線程的核心原則是:**協作式中斷**——共享變量標誌 + 阻塞點響應中斷異常 + 有序清理資源。本文詳細解析三種常見場景的標準處理模式。

核心原則

  1. 使用 volatile boolean stopFlag 或 AtomicBoolean 作為中斷信號,確保跨線程可見性。
  2. 線程主迴圈定期檢查標誌,尤其在耗時計算、I/O 間隙。
  3. 若線程可能阻塞於 wait()/sleep()/join(),需配合 interrupt() 拋出 InterruptedException 逃離阻塞。
  4. 捕獲異常後**必須重新設置中斷狀態**(Thread.currentThread().interrupt())或立即退出,避免吞噬中斷信號。
  5. 在 finally 區塊釋放鎖、關閉流、歸還連接池等清理工作。

模式一:純計算/輪詢線程 —— 僅共享標誌

public class Worker implements Runnable {
    private volatile boolean stop = false;
    public void requestStop() { stop = true; }
    public void run() {
        while (!stop) {
            // 執行單位任務
            doTask();
            // 可選:微睡眠降低 CPU 佔用
            // Thread.sleep(1); // 需捕獲 InterruptedException
        }
        cleanup();
    }
}
// 使用:
Worker w = new Worker(); new Thread(w).start();
// ... 稍後 ...
w.requestStop(); // 線程下一次迴圈檢查即退出

適用於非阻塞、可快速完成單位任務的線程。

模式二:阻塞於 sleep/wait/join —— 標誌 + interrupt()

public class Poller implements Runnable {
    private volatile boolean stop = false;
    public void requestStop() { stop = true; Thread.currentThread().interrupt(); }
    public void run() {
        while (!stop) {
            try {
                doPoll();
                Thread.sleep(1000); // 定期輪詢
            } catch (InterruptedException e) {
                // 睡眠被喚醒,檢查 stop 標誌決定是否退出
                Thread.currentThread().interrupt(); // 重置中斷狀態(良好習慣)
            }
        }
        cleanup();
    }
}

關鍵順序:**先設 stop=true,再呼叫 interrupt()**;捕獲異常後迴圈條件 !stop 自然退出。

模式三:阻塞於 I/O (ServerSocket.accept, InputStream.read) —— 關閉資源

interrupt() **無法** 喚醒傳統 BIO 阻塞 I/O(Java NIO Channel 除外)。必須關閉底層資源:

public class Server implements Runnable {
    private ServerSocket serverSocket;
    private volatile boolean stop = false;
    public void requestStop() {
        stop = true;
        try { if (serverSocket != null) serverSocket.close(); } catch (IOException ignored) {}
    }
    public void run() {
        try {
            serverSocket = new ServerSocket(8080);
            while (!stop) {
                try {
                    Socket client = serverSocket.accept(); // 阻塞點
                    handle(client);
                } catch (SocketException e) {
                    if (stop) break; // Socket 關閉導致異常,正常退出
                    throw e;
                }
            }
        } finally { closeQuietly(serverSocket); }
    }
}

同理:InputStream.read() 需關閉 InputStream;DatagramSocket.receive() 關閉 DatagramSocket。

現代替代方案:ExecutorService + Future

ExecutorService exec = Executors.newSingleThreadExecutor();
Future future = exec.submit(() -> {
    while (!Thread.currentThread().isInterrupted()) { doWork(); }
});
// 停止:
future.cancel(true); // 內部調用 interrupt()
exec.shutdown();
exec.awaitTermination(5, TimeUnit.SECONDS);

Future.cancel(true) 封裝了中斷邏輯,ExecutorService 管理線程生命週期,程式碼更簡潔健壯。

總結對照表

阻塞類型中斷方式關鍵代碼
無阻塞 / 計算volatile flagwhile(!flag) work();
Thread.sleep/wait/joinflag + interrupt()catch(InterruptedException) { if(flag) break; }
傳統 BIO I/Oflag + close() 資源socket.close() → accept() 拋 SocketException
NIO Channelflag + interrupt()channel.close() 或 interrupt() 均可觸發 ClosedByInterruptException

牢記:永遠不調用 stop()、destroy()、suspend()、resume();中斷是請求而非命令,被中斷方決定如何響應。

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