天天看點

簡述Thread的interrupt()、interrupted()及isInterrupted()的差別

前言

在java Thread類中,我們會看到interrupt()、interrupted()及isInterrupted(),在大多數情況下,我們都不會使用到它們,但是有一個​

​InterruptedException​

​類我們應該是經常會遇到的,例如:

public static void main(String[] args) {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }      

那麼你知道​

​InterruptedException​

​異常是如何觸發的嘛?

如何觸發InterruptedException

我們發現,在sleep()、wait()、join()等阻塞方法上才會需要抛​

​InterruptedException​

​:

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
      try {
        // 執行睡眠1秒,馬上會被主線程打斷
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // 觸發InterruptedException異常
        e.printStackTrace();
        // 列印是否被打斷
        System.out.println(Thread.currentThread().isInterrupted());
      }
    });
    // 主線程啟動線程t1
    t1.start();
    // 主線程打斷t1
    t1.interrupt();
    // 等等t1執行完畢
    t1.join();
  }      

1.我們建立了一個線程對象t1,t1執行邏輯就是sleep 1秒鐘;不過我們會發現線程t1根本sleep不了那麼久,因為主線程馬上就會打斷它;線程t1被打斷後,會列印出異常堆棧,并輸出線程t1的打斷标記;

2.在主線程中,啟動線程t1後,那麼就打斷線程t1;

3.等待線程t1邏輯全部執行完畢後主線程退出;

我們會發現,輸出的線程t1的打斷标記一緻是false;咱們明明已經調用了​

​t1.interrupt()​

​,并且也觸發了​

​InterruptedException​

​異常,這到底是為什麼導緻上面代碼線程t1的打斷标記一直是false呢?

我們從JDK源碼中找到了這樣一段注釋:

簡述Thread的interrupt()、interrupted()及isInterrupted()的差別

簡單翻譯如下:​

​如果任何線程打斷目前線程,目前線程的打斷标記在InterruptedException抛出時會被清除掉。​

是以說,我們在捕捉到​

​InterruptedException​

​後想要再拿到線程t1的打斷标記基本上是不可能的。

interrupt()的作用

在Thread中,我們調用​

​interrupt()​

​并不會阻止目标線程繼續執行,它隻是給目标線程打上一個标記:

public static void main(String[] args) throws InterruptedException {
    // 建立線程t1
    Thread t1 = new Thread(() -> {
      int i = 0;
      // 循環自增
      while (true) {
        System.out.println(i);
        i++;
        // 判斷是否有打斷标記
        if(Thread.currentThread().isInterrupted()){
          System.out.println("線程被打斷,跳出循環");
          // 如果有打斷标記,就跳出循環
          break;
        }
      }
    });
    // 啟動線程t1
    t1.start();
    // 打斷線程t1
    t1.interrupt();
    // 等待線程t1執行完畢
    t1.join();
  }      

1.在上述代碼中,如果删掉break代碼,那麼線程t1會一直死循環,說明interrupt()是無法阻止線程t1執行的;

2.在非阻塞代碼中,我們是可以拿到線程t1的打斷标記的,也就是說,非阻塞代碼不會清除線程的打斷标記;

interrupted()及isInterrupted()的差別

我們可以看一下Thread類中這兩個方法的源代碼:

// 靜态方法,調用目前線程的isInterrupted(true)
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    // 對象方法,調用目前線程對象的isInterrupted(false)
    public boolean isInterrupted() {
        return isInterrupted(false);
    }      

根據上面源碼,我們發現了​

​interrupted()​

​是一個靜态方法,是可以直接通過​

​Thread.interrupted()​

​調用的;​

​isInterrupted()​

​方法是線程對象方法,是需要通過線程對象調用的;我們在前面代碼中使用​

​Thread.currentThread().isInterrupted()​

​就是通過線程對象調用的;

另一個差別就是兩個方法傳遞的參數不同,​

​interrupted()​

​傳遞的true,​

​isInterrupted()​

​傳遞的是false;這兩個參數的作用是​

​是否清除打斷标記​

​,也就是說,如果調用​

​Thread.interrupted()​

​傳回true後,我們的打斷标記會被清除,那麼再次調用​

​Thread.interrupted()​

​拿到的就是false;​

​isInterrupted()​

​方法就不會清除打斷标記,每次調用​

​isInterrupted()​

​結果都不變;

小結

通過上述示例示範,我們可以總結出如下幾點: