官术网_书友最值得收藏!

1.7 線程中斷

Java中的線程中斷是一種線程間的協作模式,通過設置線程的中斷標志并不能直接終止該線程的執行,而是被中斷的線程根據中斷狀態自行處理。

● void interrupt()方法 :中斷線程,例如,當線程A運行時,線程B可以調用線程A的interrupt()方法來設置線程A的中斷標志為true并立即返回。設置標志僅僅是設置標志,線程A實際并沒有被中斷,它會繼續往下執行。如果線程A因為調用了wait系列函數、join方法或者sleep方法而被阻塞掛起,這時候若線程B調用線程A的interrupt()方法,線程A會在調用這些方法的地方拋出InterruptedException異常而返回。

● boolean isInterrupted()方法:檢測當前線程是否被中斷,如果是返回true,否則返回false。

public boolean isInterrupted() {
      //傳遞false,說明不清除中斷標志
      return isInterrupted(false);
  }

● boolean interrupted()方法:檢測當前線程是否被中斷,如果是返回true,否則返回false。與isInterrupted不同的是,該方法如果發現當前線程被中斷,則會清除中斷標志,并且該方法是static方法,可以通過Thread類直接調用。另外從下面的代碼可以知道,在interrupted()內部是獲取當前調用線程的中斷標志而不是調用interrupted()方法的實例對象的中斷標志。

public static boolean interrupted() {
  //清除中斷標志
  return currentThread().isInterrupted(true);
}

下面看一個線程使用Interrupted優雅退出的經典例子,代碼如下。

public void run(){
    try{
        ....
        //線程退出條件
        while(! Thread.currentThread().isInterrupted()&& more work to do){
                // do more work;
        }
    }catch(InterruptedException e){
                // thread was interrupted during sleep or wait
    }
    finally{
              // cleanup, if required
    }
}

下面看一個根據中斷標志判斷線程是否終止的例子。

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            //如果當前線程被中斷則退出循環
            while (! Thread.currentThread().isInterrupted())
                System.out.println(Thread.currentThread() + " hello");
        }
    });
    //啟動子線程
    thread.start();
    //主線程休眠1s,以便中斷前讓子線程輸出
    Thread.sleep(1000);
    //中斷子線程
    System.out.println("main thread interrupt thread");
    thread.interrupt();
    //等待子線程執行完畢
    thread.join();
    System.out.println("main is over");
}

輸出結果如下.

在如上代碼中,子線程thread通過檢查當前線程中斷標志來控制是否退出循環,主線程在休眠1s后調用thread的interrupt()方法設置了中斷標志,所以線程thread退出了循環。

下面再來看一種情況。當線程為了等待一些特定條件的到來時,一般會調用sleep函數、wait系列函數或者join()函數來阻塞掛起當前線程。比如一個線程調用了Thread. sleep(3000),那么調用線程會被阻塞,直到3s后才會從阻塞狀態變為激活狀態。但是有可能在3s內條件已被滿足,如果一直等到3s后再返回有點浪費時間,這時候可以調用該線程的interrupt()方法,強制sleep方法拋出InterruptedException異常而返回,線程恢復到激活狀態。下面看一個例子。

public static void main(String[] args) throws InterruptedException {
      Thread threadOne = new Thread(new Runnable() {
          public void run() {
              try {
                  System.out.println("threadOne begin sleep for 2000 seconds");
                  Thread.sleep(2000000);
                  System.out.println("threadOne awaking");
              } catch (InterruptedException e) {
                  System.out.println("threadOne is interrupted while sleeping");
                  return;
              }
              System.out.println("threadOne-leaving normally");
          }
      });
      //啟動線程
      threadOne.start();
      //確保子線程進入休眠狀態
      Thread.sleep(1000);
      //打斷子線程的休眠,讓子線程從sleep函數返回
      threadOne.interrupt();
      //等待子線程執行完畢
      threadOne.join();
      System.out.println("main thread is over");
}

輸出結果如下。

在如上代碼中,threadOne線程休眠了2000s,在正常情況下該線程需要等到2000s后才會被喚醒,但是本例通過調用threadOne.interrupt()方法打斷了該線程的休眠,該線程會在調用sleep方法處拋出InterruptedException異常后返回。

下面再通過一個例子來了解interrupted()與isInterrupted()方法的不同之處。

public static void main(String[] args) throws InterruptedException {
    Thread threadOne = new Thread(new Runnable() {
        public void run() {
              for(; ; ){
              }
        }
    });
    //啟動線程
    threadOne.start();
    //設置中斷標志
    threadOne.interrupt();
    //獲取中斷標志
    System.out.println("isInterrupted:" + threadOne.isInterrupted());
    //獲取中斷標志并重置
    System.out.println("isInterrupted:" + threadOne.interrupted());
    //獲取中斷標志并重置
    System.out.println("isInterrupted:" + Thread.interrupted());
    //獲取中斷標志
    System.out.println("isInterrupted:" + threadOne.isInterrupted());
    threadOne.join();
    System.out.println("main thread is over");
}

輸出結果如下。

第一行輸出true這個大家應該都可以想到,但是下面三行為何是false、false、true呢,不應該是true、false、false嗎?如果你有這個疑問,則說明你對這兩個函數的區別還是不太清楚。上面我們介紹了在interrupted()方法內部是獲取當前線程的中斷狀態,這里雖然調用了threadOne的interrupted()方法,但是獲取的是主線程的中斷標志,因為主線程是當前線程。threadOne.interrupted()和Thread.interrupted()方法的作用是一樣的,目的都是獲取當前線程的中斷標志。修改上面的例子為如下。

public static void main(String[] args) throws InterruptedException {
    Thread threadOne = new Thread(new Runnable() {
        public void run() {
            //中斷標志為true時會退出循環,并且清除中斷標志
            while (! Thread.currentThread().interrupted()) {
            }
            System.out.println("threadOne isInterrupted:" + Thread.currentThread().
isInterrupted());
        }
    });
    // 啟動線程
    threadOne.start();
    // 設置中斷標志
    threadOne.interrupt();
    threadOne.join();
    System.out.println("main thread is over");
}

輸出結果如下。

由輸出結果可知,調用interrupted()方法后中斷標志被清除了。

主站蜘蛛池模板: 名山县| 弥勒县| 肥东县| 盘锦市| 定边县| 韶山市| 中卫市| 郑州市| 连江县| 元朗区| 同德县| 潮安县| 全州县| 鸡西市| 民勤县| 孟连| 太谷县| 焦作市| 句容市| 侯马市| 太保市| 渝中区| 砚山县| 英超| 开原市| 咸阳市| 慈溪市| 西安市| 安新县| 孝昌县| 星子县| 揭东县| 博白县| 蒲江县| 姜堰市| 郸城县| 石楼县| 司法| 望奎县| 崇义县| 汤原县|