Thread類方法之二--interrupt

interrupt 方法打斷線程

  • case1:打斷sleep, wait, join (阻塞)的線程
  • case2:打斷運行中的線程

打斷阻塞狀態中的線程,會拋出一個InterruptedException異常,並且會重置打斷標記

  • 介紹一下打斷標記

Thread類裏有一個isInterrupted方法可以返回當前線程是否被打斷,被打斷返回true,否則false

public boolean isInterrupted() {
        return isInterrupted(false); // 這裏的false指不會重置打斷標記
    }

還有一個類似的靜態方法interrupted也可以返回當前線程是否被打斷,被打斷返回true,否則false,但是會重置打斷標記爲false。

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);  // 這裏的true指會重置打斷標記
    }
打斷阻塞狀態的線程
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("sleep...");
            try {
                Thread.sleep(5000); // wait, join
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1");

        t1.start();
        Thread.sleep(1000);
        log.debug("interrupt");
        log.debug("打斷標記:{}", t1.isInterrupted()); // 1
        t1.interrupt();
        log.debug("打斷標記:{}", t1.isInterrupted()); // 2
    }
}

/*Output:
22:29:08.624 c.Test11 [t1] - sleep...
22:29:09.621 c.Test11 [main] - interrupt
22:29:09.621 c.Test11 [main] - 打斷標記:false
22:29:09.624 c.Test11 [main] - 打斷標記:false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at cn.itcast.test.Test11.lambda$main$0(Test11.java:12)
	at java.lang.Thread.run(Thread.java:748)
*/

上面兩個false是意義不同的,第一個代表當前線程正常運行,沒有被打斷;而第二個false代表線程在sleep, wait, join 中(阻塞狀態)被打斷,會拋出異常並將打斷標記重置爲false。

打斷正常運行的線程
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while(true) {
                System.out.println("isInterrupted狀態: " + (Thread.currentThread().isInterrupted()));
            }
        }, "t1");
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
    }
}

/*Output:
...
isInterrupted狀態: false
isInterrupted狀態: false
isInterrupted狀態: false
22:43:30.383 c.Test12 [main] - interrupt
isInterrupted狀態: true
isInterrupted狀態: true
isInterrupted狀態: true
...

打斷後標記變爲true, 但是進程並沒有停止,那麼這個打斷到底有啥用啊?我們怎麼讓被打斷後的進程停下來哪?我們可以藉助打斷標記,如果打斷標記爲真,就手動退出進程。

public class Test {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while(true) {
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted) {
                    log.debug("被打斷了, 退出循環");
                    break;
                }
            }
        }, "t1");
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
    }
}

/*Output:
22:52:17.742 c.Test12 [main] - interrupt
22:52:17.747 c.Test12 [t1] - 被打斷了, 退出循環
*/

interrupt應用:多線程設計模式之兩階段終止

Two Phase Termination :在一個線程 T1 中如何“優雅”終止線程 T2?這裏的【優雅】指的是給 T2 一個料理後事的機會。

錯誤思路
  • 使用線程對象的 stop() 方法停止線程
    • stop 方法會真正殺死線程,如果這時線程鎖住了共享資源,那麼當它被殺死後就再也沒有機會釋放鎖, 其它線程將永遠無法獲取鎖
  • 使用 System.exit(int) 方法停止線程
    • 目的僅是停止一個線程,但這種做法會讓整個程序都停止
兩階段終止模式
  • 應用場景:後臺監控線程(需要設置停止選項)
  • 思路
    在這裏插入圖片描述
@Slf4j(topic = "c.TwoPhaseTermination")
public class Test_1 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination_1 tpt = new TwoPhaseTermination_1();
        tpt.start();

        Thread.sleep(3500);
        log.debug("停止監控");
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination_1 {
    // 監控線程
    private Thread monitorThread;

    // 啓動監控線程
    public void start() {
        monitorThread = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                // 是否被打斷
                if (current.isInterrupted()) {
                    log.debug("料理後事");
                    break;
                }
                try {
                    Thread.sleep(1000);          // 睡眠時被打斷
                    log.debug("執行監控記錄");   // 正常打斷
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 當拋出異常時,重新設置打斷標記
                    current.interrupt();
                }
            }
        }, "monitor");
        monitorThread.start();
    }

    // 停止監控線程
    public void stop() {
        monitorThread.interrupt();
    }
}

/*Output:
00:30:54.263 c.TwoPhaseTermination [monitor] - 執行監控記錄
00:30:55.263 c.TwoPhaseTermination [monitor] - 執行監控記錄
00:30:56.265 c.TwoPhaseTermination [monitor] - 執行監控記錄
00:30:56.765 c.TwoPhaseTermination [main] - 停止監控
00:30:56.765 c.TwoPhaseTermination [monitor] - 料理後事
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at cn.itcast.test.TwoPhaseTermination_1.lambda$start$0(Test_1.java:33)
	at java.lang.Thread.run(Thread.java:748)
*/

在睡眠時被打斷,拋出異常並優雅的退出線程。正常運行時被打斷,優雅的退出線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章