執行緒中斷停止執行緒

每個 Java 執行緒都有一箇中斷標誌,最初為 false。中斷執行緒,基本上只是將該標誌設定為 true。在該執行緒上執行的程式碼可以偶爾檢查該標誌並對其進行操作。程式碼也可以完全忽略它。但為什麼每個 Thread 都有這樣的標誌呢?畢竟,線上程上有一個布林標誌是我們可以自己組織的東西,如果需要的話。好吧,有些方法在它們執行的​​執行緒被中斷時以特殊方式執行。這些方法稱為阻塞方法。這些是將執行緒置於 WAITING 或 TIMED_WAITING 狀態的方法。當一個執行緒處於這種狀態時,中斷它會導致在被中斷的執行緒上丟擲 InterruptedException,而不是將中斷標誌設定為 true,並且執行緒再次變為 RUNNABLE。呼叫阻塞方法的程式碼被強制處理 InterruptedException,因為它是一個經過檢查的異常。因此,因此它的名稱,中斷可以具有中斷 WAIT 的效果,從而有效地結束它。請注意,並非所有以某種方式等待的方法(例如阻塞 IO)都以這種方式響應中斷,因為它們不會使執行緒處於等待狀態。最後,設定了中斷標誌的執行緒進入阻塞方法(即嘗試進入等待狀態),將立即丟擲 InterruptedException,並清除中斷標誌。請注意,並非所有以某種方式等待的方法(例如阻塞 IO)都以這種方式響應中斷,因為它們不會使執行緒處於等待狀態。最後,設定了中斷標誌的執行緒進入阻塞方法(即嘗試進入等待狀態),將立即丟擲 InterruptedException,並清除中斷標誌。請注意,並非所有以某種方式等待的方法(例如阻塞 IO)都以這種方式響應中斷,因為它們不會使執行緒處於等待狀態。最後,設定了中斷標誌的執行緒進入阻塞方法(即嘗試進入等待狀態),將立即丟擲 InterruptedException,並清除中斷標誌。

除了這些機制之外,Java 並沒有為中斷分配任何特殊的語義含義。程式碼可以任意方式自由解釋中斷。但大多數情況下,中斷通常用於向執行緒發出訊號,它應該儘早停止執行。但是,正如上面應該清楚的那樣,該執行緒上的程式碼可以適當地對該中斷作出反應以便停止執行。停止執行緒是一種協作。當一個執行緒被中斷時,它的執行程式碼可以在堆疊跟蹤的幾個深層。大多數程式碼都不會呼叫阻塞方法,並且足夠及時完成,不會過度延遲執行緒的停止。應該主要關注響應中斷的程式碼是迴圈處理任務的程式碼,直到沒有剩下的程式碼為止,或直到設定一個標誌,表示它停止該迴圈。處理可能無限任務的迴圈(即它們原則上保持執行)應檢查中斷標誌以退出迴圈。對於有限迴圈,語義可能要求所有任務必須在結束之前完成,或者保留一些未處理的任務可能是適當的。呼叫阻塞方法的程式碼將被強制處理 InterruptedException。如果在語義上完全可能,它可以簡單地傳播 InterruptedException 並宣告丟擲它。因此,就其呼叫者而言,它本身就成為一種阻止方法。如果它不能傳播異常,它應該至少設定中斷標誌,因此堆疊上方的呼叫者也知道執行緒被中斷。在某些情況下,無論 InterruptedException 如何,該方法都需要繼續等待,

例子 :

在中斷時停止處理任務的程式碼示例

class TaskHandler implements Runnable {
    
    private final BlockingQueue<Task> queue;

    TaskHandler(BlockingQueue<Task> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) { // check for interrupt flag, exit loop when interrupted
            try {
                Task task = queue.take(); // blocking call, responsive to interruption
                handle(task);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // cannot throw InterruptedException (due to Runnable interface restriction) so indicating interruption by setting the flag
            }
        }
    }
    
    private void handle(Task task) {
        // actual handling
    }
}

延遲設定中斷標誌直到完成的程式碼示例:

class MustFinishHandler implements Runnable {

    private final BlockingQueue<Task> queue;

    MustFinishHandler(BlockingQueue<Task> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        boolean shouldInterrupt = false;
        
        while (true) {
            try {
                Task task = queue.take();
                if (task.isEndOfTasks()) {
                    if (shouldInterrupt) {
                        Thread.currentThread().interrupt();
                    }
                    return;
                }
                handle(task);
            } catch (InterruptedException e) {
                shouldInterrupt = true; // must finish, remember to set interrupt flag when we're done
            }
        }
    }

    private void handle(Task task) {
        // actual handling
    }
}

具有固定任務列表但可能在中斷時提前退出的程式碼示例

class GetAsFarAsPossible implements Runnable {

    private final List<Task> tasks = new ArrayList<>();

    @Override
    public void run() {
        for (Task task : tasks) {
            if (Thread.currentThread().isInterrupted()) {
                return;
            }
            handle(task);
        }
    }

    private void handle(Task task) {
        // actual handling
    }
}