多線程 - 如何正確的終止線程?interrupt()、interrupted()、isInterrupted()區別?


總結

java線程之間是協同式,不是搶占式

  

因為是協同式,所以線程之間都是“商量”着來,最佳實踐是沒有誰強迫誰終止的情況的,因此才會淘汰stop()方法,詳見 多線程 - 為何要棄用stop() suspend()?(不過你硬要調用stop()就另當別論...)

  • public void interrupt()  會為指定線程設置一個interrupted狀態 -- 和stop()不同,不會強制線程停止,只會設置一個“interrupt標志位”,置為true(連續調用還是true,不會有異常發生。對比 Thread.start(), 多次調用會出現java.lang.IllegalStateException)。代表着“該線程被建議終止”。但是是否理會該建議,完全看該線程的具體邏輯是否去檢查這個標志位,然后根據標志做出不同的反應。也有可能該線程的業務邏輯完全不理會標志位的變化,也就會繼續我行我素的運行。
  • public static boolean interrupted() 會判斷當前線程的interrupted狀態,並且會重置該狀態 -- 檢查是否當前線程被設置了“interrupt標志位”。如果被設置了,返回true,反之返回false。之后,該標志位會被清除
  • public boolean isInterrupted() 會判斷指定線程的interrupted狀態 -- 檢查是否當前線程被設置了“interrupt標志位”。如果被設置了,返回true,反之返回false。但是,該函數不會清除標志位。

 

    @Test
    public void testInterrupted(){
        boolean b1 = Thread.interrupted();  //false
        Thread.currentThread().interrupt();
        boolean b2 = Thread.interrupted();  //true
        boolean b3 = Thread.interrupted();  //false
    }

    @Test
    public void testIsInterrupted(){
        boolean b4 = Thread.currentThread().isInterrupted(); //false
        Thread.currentThread().interrupt();
        boolean b5 = Thread.currentThread().isInterrupted(); //true
        boolean b6 = Thread.currentThread().isInterrupted(); //true
    }

  

 

調用interrupt()拋出異常的情況(必看)

對於interrupt()方法,根據官方文檔:

  • 如果指定線程調用了interrupt(),並且該線程不處於被sleep,wait,join的狀態,那么一切正常,不會拋出調用InterruptException異常,就不會清除中斷標志位。如果此時再調用isInterrupted()就會返回true。
  • 如果指定線程調用了interrupt()時,而指定線程正處於阻塞狀態sleep,wait,join...線程自己就會立即拋InterruptException異常。
    • 原本“interrupt標志位”應該是true(因為之前調用了interrupt()),也會被清除變為false。如果再調用isInterrupted()返回false。
    • 如果你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態;如果你的程序捕獲了這個異常,那么程序就會繼續執行catch語句塊(可能還有finally語句塊)以及以后的代碼。
    • 需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用interrupt()時,如果該線程正在執行普通的代碼,那么該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到wait()/sleep()/join()后,就會立刻拋出InterruptedException。

 

Q:為什么當指定線程在被阻塞狀態(sleep, wait)時,調用interrupt(),要立即拋出InterruptException異常,並清除“interrupt標志位”(從true變為false)

A:【本答案不是很嚴謹,請大家斧正】線程在被阻塞過程中,可能掌握着某種資源。如果線程剛從阻塞回來,“interrupt標志位”就是true,很可能(視用戶自定義的代碼而定)線程就真准備中斷,沒有給程序任何回收資源的時間。因此先取消標志位,回收下資源然后視情況再決定是否真的interrupt。

 

如果中斷sleep,wait,join等,就會拋InterruptException異常,就會清除中斷標志位,那么這種情況應該怎么處理呢?為了保證數據的一致性和完整性,我們需要用Thread.interrupt()方法再次中斷自己,置上中斷標志位。例子如下:

    @Test
    public void testInterrupt() throws InterruptedException {
        Thread t1 = new Thread() {
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Interruted!");
                        break;
                    }
                    try {
                        Thread.sleep(2000); // 睡眠時中斷會清除中斷標志位
                    } catch (InterruptedException e) {
                        // 如果少了下面這句,這個線程雖然在外面中斷,但是只要中斷睡眠中的進程
                        // 就會清除中斷標志位,仍然處於無限循環,會競爭CPU資源
                        Thread.currentThread().interrupt(); // 再次中斷置上中斷標記
                    }
                    Thread.yield();
                }
            }
        };
        t1.start();       //t1 thread would running
        Thread.sleep(200);//sleep main thread
        t1.interrupt();   //after main thread wake up, would plan to interrupt t1 thread
    }

  

 

interrupt()官方文檔

中斷線程。

如果當前線程沒有中斷它自己(這在任何情況下都是允許的),則該線程的 checkAccess 方法就會被調用,這可能拋出 SecurityException。

如果線程在調用 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。

如果該線程在可中斷的通道上的 I/O 操作中受阻,則該通道將被關閉,該線程的中斷狀態將被設置並且該線程將收到一個 ClosedByInterruptException。

如果該線程在一個 Selector 中受阻,則該線程的中斷狀態將被設置,它將立即從選擇操作返回,並可能帶有一個非零值,就好像調用了選擇器的 wakeup 方法一樣。

如果以前的條件都沒有保存,則該線程的中斷狀態將被設置。

中斷一個不處於活動狀態的線程不需要任何作用。

拋出:

SecurityException - 如果當前線程無法修改該線程

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM