1. 程式人生 > >泥瓦匠聊併發程式設計基礎篇:執行緒中斷和終止

泥瓦匠聊併發程式設計基礎篇:執行緒中斷和終止

1 執行緒中斷

1.1 什麼是執行緒中斷?

執行緒中斷是執行緒的標誌位屬性。而不是真正終止執行緒,和執行緒的狀態無關。執行緒中斷過程表示一個執行中的執行緒,通過其他執行緒呼叫了該執行緒的 interrupt() 方法,使得該執行緒中斷標誌位屬性改變。

深入思考下,執行緒中斷不是去中斷了執行緒,恰恰是用來通知該執行緒應該被中斷了。具體是一個標誌位屬性,到底該執行緒生命週期是去終止,還是繼續執行,由執行緒根據標誌位屬性自行處理。

1.2 執行緒中斷操作

呼叫執行緒的 interrupt() 方法,根據執行緒不同的狀態會有不同的結果。

下面新建 InterruptedThread 物件,程式碼如下:

/**
 * 一直執行的執行緒,中斷狀態為 true
 *
 * @author Jeff Lee @ bysocket.com
 * @since 2018年02月23日19:03:02
 */
public class InterruptedThread implements Runnable {

    @Override // 可以省略
    public void run() {
        // 一直 run
        while (true) {
        }
    }

    public static void main(String[] args)
throws Exception
{ Thread interruptedThread = new Thread(new InterruptedThread(), "InterruptedThread"); interruptedThread.start(); TimeUnit.SECONDS.sleep(2); interruptedThread.interrupt(); System.out.println("InterruptedThread interrupted is " + interruptedThread.isInterrupted()); TimeUnit.SECONDS.sleep(2
); } }

執行 main 函式,結果如下:

InterruptedThread interrupted is true

程式碼詳解:

  • 執行緒一直在執行狀態,沒有停止或者阻塞等
  • 呼叫了 interrupt() 方法,中斷狀態置為 true,但不會影響執行緒的繼續執行

另一種情況,新建 InterruptedException 物件,程式碼如下:

/**
 * 丟擲 InterruptedException 的執行緒,中斷狀態被重置為預設狀態 false
 *
 * @author Jeff Lee @ bysocket.com
 * @since 2018年02月23日19:03:02
 */
public class InterruptedException implements Runnable {

    @Override // 可以省略
    public void run() {
        // 一直 sleep
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (java.lang.InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        Thread interruptedThread = new Thread(new InterruptedException(), "InterruptedThread");
        interruptedThread.start();

        TimeUnit.SECONDS.sleep(2);

        // 中斷被阻塞狀態(sleep、wait、join 等狀態)的執行緒,會丟擲異常 InterruptedException
        // 在丟擲異常 InterruptedException 前,JVM 會先將中斷狀態重置為預設狀態 false
        interruptedThread.interrupt();
        System.out.println("InterruptedThread interrupted is " + interruptedThread.isInterrupted());
        TimeUnit.SECONDS.sleep(2);
    }
}

執行 main 函式,結果如下:

InterruptedThread interrupted is false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)

程式碼詳解:

  • 中斷被阻塞狀態(sleep、wait、join 等狀態)的執行緒,會丟擲異常 InterruptedException
  • 丟擲異常 InterruptedException 前,JVM 會先將中斷狀態重置為預設狀態 false

小結下執行緒中斷:

  • 執行緒中斷,不是停止執行緒,只是一個執行緒的標誌位屬性
  • 如果執行緒狀態為被阻塞狀態(sleep、wait、join 等狀態),執行緒狀態退出被阻塞狀態,丟擲異常 InterruptedException,並重置中斷狀態為預設狀態 false
  • 如果執行緒狀態為執行狀態,執行緒狀態不變,繼續執行,中斷狀態置為 true

2 執行緒終止

比如在 IDEA 中強制關閉程式,立即停止程式,不給程式釋放資源等操作,肯定是不正確的。執行緒終止也存在類似的問題,所以需要考慮如何終止執行緒?

上面聊到了執行緒中斷,可以利用執行緒中斷標誌位屬性來安全終止執行緒。同理也可以使用 boolean 變數來控制是否需要終止執行緒。

新建 ,程式碼如下:

/**
 * 安全終止執行緒
 *
 * @author Jeff Lee @ bysocket.com
 * @since 2018年02月23日19:03:02
 */
public class ThreadSafeStop {

    public static void main(String[] args) throws Exception {
        Runner one = new Runner();
        Thread countThread = new Thread(one, "CountThread");
        countThread.start();
        // 睡眠 1 秒,通知 CountThread 中斷,並終止執行緒
        TimeUnit.SECONDS.sleep(1);
        countThread.interrupt();

        Runner two = new Runner();
        countThread = new Thread(two,"CountThread");
        countThread.start();
        // 睡眠 1 秒,然後設定執行緒停止狀態,並終止執行緒
        TimeUnit.SECONDS.sleep(1);
        two.stopSafely();
    }

    private static class Runner implements Runnable {

        private long i;

        // 終止狀態
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                // 執行緒執行具體邏輯
                i++;
            }
            System.out.println("Count i = " + i);
        }

        public void stopSafely() {
            on = false;
        }
    }
}

從上面程式碼可以看出,通過 while (on && !Thread.currentThread().isInterrupted()) 程式碼來實現執行緒是否跳出執行邏輯,並終止。但是疑問點就來了,為啥需要 on 和 isInterrupted() 兩項一起呢?用其中一個方式不就行了嗎?答案在下面

  • 執行緒成員變數 on 通過 volatile 關鍵字修飾,達到執行緒之間可見,從而實現執行緒的終止。但當執行緒狀態為被阻塞狀態(sleep、wait、join 等狀態)時,對成員變數操作也阻塞,進而無法執行安全終止執行緒
  • 為了處理上面的問題,引入了 isInterrupted(); 只去解決阻塞狀態下的執行緒安全終止。
  • 兩者結合是真的沒問題了嗎?不是的,如果是網路 io 阻塞,比如一個 websocket 一直再等待響應,那麼直接使用底層的 close 。

3 小結

很多好友介紹,如果用 Spring 棧開發到使用執行緒或者執行緒池,那麼儘量使用框架這塊提供的執行緒操作及框架提供的終止等

資料: 《Java 併發程式設計的藝術》