1. 程式人生 > 實用技巧 >C# 非泛型集合的簡單迭代

C# 非泛型集合的簡單迭代

技術標籤:java

Java執行緒中斷

什麼是執行緒中斷

​ 中斷是給執行緒的一個指示,告訴它應該停止正在做的事並去做其他事情(一般都是結束這個執行緒)。比如360正在執行防毒掃描,我們點選“停止檢測”就是一次執行緒中斷。一個執行緒究竟要怎麼響應中斷請求取決於程式設計師,如果這個執行緒需要處理的任務很重要,那麼執行緒可以忽略這個中斷提醒。但一般相應中斷的方法都是將該執行緒終止。

中斷原理

​ 每個執行緒物件中都有一個boolean型別的狀態位(不是Thread類中的欄位,應該在C++原始碼中),表示是否有中斷請求(該請求可以來自所有執行緒,包括被中斷的執行緒本身)。執行緒在執行某些方法時,總會不時的檢測中斷位,看看有沒有中斷請求,如果有就進行相應的處理。

​ Java 中斷機制是一種協作機制,也就是說通過中斷並不能直接終止另一個執行緒(僅僅是改變了中斷位),而需要被中斷的執行緒自己處理中斷(可以忽略,也可以終止自己)。

中斷位

獲取中斷位狀態

主要有兩種方法:

  • 一種是Thread的靜態方法interrupted,該方法會檢測執行緒是否有中斷請求,如果中斷位為true,即目前該執行緒有中斷請求,那麼該方法會返回true並且將中斷位重新設定位預設值false。
  • 另一種是Thread物件的例項方法,isInterrupted。該方法也會檢測執行緒是否有中斷請求,這一點與interrupted方法相同。但不同的是,isInterrupted不會改變中斷位的狀態。

改變中斷位狀態

也是兩種:

  • 主要是通過Thread物件的例項方法:interrupt()。呼叫該方法後,執行緒的中斷位會被設定為true,但並不是真正的中斷了該執行緒。
  • 也可以通過interrupted()方法將中斷位設定為false。

中斷的程式碼實現

主要可以分為兩大類,一種是執行緒呼叫非阻塞方法時發生中斷,另一種是呼叫阻塞方式時發生中斷。當執行緒在執行方法時發生中斷,那麼就會丟擲InterruptedException異常,並且將狀態位設定位false。

阻塞方法

丟擲InterruptedException的方法叫做阻塞方法.同樣,不丟擲InterruptedException的方法叫做非阻塞方法

執行非阻塞方法時發生中斷

public class InterruptTest01 {
    public static void main(String[] args) throws InterruptedException {
        Thread interruptThread = new Thread(()->{
            System.out.println("interruptThread執行緒正在執行。。。。。。");
            while (true) {
                if (Thread.currentThread().isInterrupted()){
                    System.out.println("檢測到中斷訊號,終止執行緒");
                    // 或者其他處理
                    return;
                }
            }
        }, "interruptThread");
        interruptThread.start();
        TimeUnit.SECONDS.sleep(5);
        System.out.println(Thread.currentThread().getName() + "執行緒向" + interruptThread.getName() + "執行緒發出中斷訊號。。。。");
        interruptThread.interrupt();
    }
}

image-20201215164744899

執行阻塞方法時發生中斷

package thread;

import java.sql.Time;
import java.util.concurrent.TimeUnit;

/**
 * @author: OnlyOne
 * @create: 2020-12-15 15:52
 * @description:
 **/
public class InterruptTest02 {
    public static void main(String[] args) throws InterruptedException {
        Thread interruptThread = new Thread(()->{
            try {
                while (true) {
                    System.out.println("interruptThread執行緒即將沉睡5秒。。。。。");
                    TimeUnit.SECONDS.sleep(5);
                }
            }catch (InterruptedException e) {
                    System.out.println("捕獲到InterruptedException異常。。。。");
                    System.out.println("此時中斷位:" + Thread.currentThread().isInterrupted());
                    return;
                }
        }, "interruptThread");
        interruptThread.start();

        // main執行緒沉睡兩秒,好讓interruptThread執行緒沉睡
        TimeUnit.SECONDS.sleep(2);

        System.out.println(Thread.currentThread().getName() + "執行緒向" + interruptThread.getName() + "執行緒發出中斷訊號。。。。");
        interruptThread.interrupt();
    }
}

image-20201215171424776

以上程式中,當main執行緒向interruptThread執行緒發出中斷請求後,interruptThread的狀態位被設定為true。但此時interruptThread執行緒正在呼叫阻塞方法,因此就會丟擲InterruptedException異常。

如何處理InterruptedException

當執行緒在執行時需要處理InterruptedException異常時,不要簡單的將其忽略,因為丟擲InterruptedException異常後該執行緒的中斷位就會被擦除(如果沒有對此中斷進行處理),設定為預設的false,這樣就會造成中斷請求的丟失。比如使用try catch捕獲異常,但在catch中不對中斷進行處理。

這種情況下就有兩種比較可取的方式:

  • 第一種,在catch塊中呼叫interrupt(),從新將中斷位置為true,這樣以後還可以檢測出該中斷
  • 第二種,不要catch這個異常,使用throws宣告,讓方法的呼叫者去處理。

參考

https://www.infoq.cn/article/java-interrupt-mechanism/

https://www.jianshu.com/p/a8abe097d4ed

https://ifeve.com/java-interrupt/

https://ifeve.com/thread-management-4/