協做,才能更好的中斷線程

聊起中斷,你們可能最熟悉的例子就是線程休眠。下面就是一個線程休眠的 demo,在這個例子中,當咱們調用 sleep 方法,該方法將會拋出一個須要捕獲的中斷異常,這裏捕獲該異常並直接返回。html

for (int i = 0; i < somethings.size(); i++) {
            // 休眠 4 s
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // 拋出中斷異常
                return;
            }
            // 輸出
            System.out.println(somethings.get(i));
        }

除了 InterruptedException 中斷異常,另外還有三個中斷相關的方法,三個方法都與線程相關。java

  • thread#interrupt
  • Thread#interrupted
  • thread#isInterrupted

interrupt 方法用於中斷線程,可是並非說該方法就能直接使線程中止面試

下面使用 interrupt 中斷線程,這裏咱們指望中斷直接中止子線程輸出。可是當主線程調用子線程 interrupt 方法,子線程並卻沒有被終止,還在繼續打印數字。ide

Runnable interruptedTask=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <Integer.MAX_VALUE ; i++) {
                    System.out.println(i);
                }
            }
        };

        Thread interruptThread=new Thread(interruptedTask);
        interruptThread.start();
        // 休眠 5 s,
        TimeUnit.SECONDS.sleep(2);
        // 中斷當前線程
        interruptThread.interrupt();
        // 再次休眠,觀察子線程
        TimeUnit.SECONDS.sleep(2);

引用 Java 官方對於中斷的解釋:idea

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate

中斷僅僅只是代表這個線程能夠中止,可是線程是否中止徹底取決於線程本身。只有線程相互協做,才能更好的中止線程。spa

每一個線程都包含一個內部標誌,用來表示中斷狀態。調用線程的 interrupt 方法將會設置該狀態位,對於 Thread#sleep 等阻塞方法,將會拋出 InterruptedException ,並清除中斷標誌。線程

咱們可使用 thread#isInterruptedThread#interrupted 檢查中斷狀態。可是須要注意,兩個方法存在一些區別,Thread#interrupted 爲靜態類方法,該方法檢測到中斷以後就會清除中斷標誌。日誌

上面的方法咱們只要加上中斷狀態判斷就也能夠中止線程。code

Runnable interruptedTask=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <Integer.MAX_VALUE ; i++) {
                    // 一旦檢測到中斷標誌,中止線程
                    if(Thread.interrupted()){
                        System.out.println("interrupted!!!!");
                        break;
                    }
                    System.out.println(i);
                }
            }
        };

        Thread interruptThread=new Thread(interruptedTask);
        interruptThread.start();
        // 休眠 5 s,
        TimeUnit.SECONDS.sleep(2);
        // 中斷當前線程
        interruptThread.interrupt();
        // 再次休眠,觀察子線程
        TimeUnit.SECONDS.sleep(2);
    }

中斷最佳實踐

不要隨意『吃掉』中斷異常

因爲中斷異常是一個 checked exception,咱們不得不須要處理該異常。若是咱們能夠保證該異常不影響應用,咱們能夠直接『吃掉』這個異常。其餘狀況下咱們須要正確處理這個異常。htm

最簡單的作法就是不處理該異常,直接向上拋出中斷異常,讓上層調用者決定如何處理。

可是有些狀況下,卻不適合上面的作法,這種狀況下咱們須要在 catch 中處理中斷。若是實在不知道如何處理,那就是記錄該異常,並使用日誌方式輸出。

中斷不會中止阻塞 IO

上面咱們說到,對於一些阻塞方法如 Thread#sleep ,將會拋出中斷異常。可是對於 Socket 等阻塞 IO 調用,並不會拋出這個異常。也就是說中斷並不會中止阻塞 IO 的調用。

這是由於當調用 Thread#sleep 等阻塞方法時,Java 線程狀態將會從 RUNNABLE 轉變爲 TIMED_WAITINGWATTING。而當線程阻塞在 IO 讀取時,Java 線程實際狀態卻仍是 RUNNABLE。若是你對這個線程狀態還有疑惑,能夠閱讀下這篇文章 面試官:都說阻塞 I/O 模型將會使線程休眠,爲何 Java 線程狀態倒是 RUNNABLE?,深刻理解一下線程狀態。

本文首發於studyidea.cn

歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客: studyidea.cn

其餘平臺.png

相關文章
相關標籤/搜索