Java併發之線程中斷

     前面的幾篇文章主要介紹了線程的一些最基本的概念,包括線程的間的衝突及其解決辦法,以及線程間的協做機制。本篇主要來學習下Java中對線程中斷機制的實現。在咱們的程序中常常會有一些不達到目的不會退出的線程,例如:咱們有一個下載程序線程,該線程在沒有下載成功以前是不會退出的,若此時用戶以爲下載速度慢,不想下載了,這時就須要用到咱們的線程中斷機制了,告訴線程,你不要繼續執行了,準備好退出吧。固然,線程在不一樣的狀態下遇到中斷會產生不一樣的響應,有點會拋出異常,有的則沒有變化,有的則會結束線程。本篇將從如下兩個方面來介紹Java中對線程中斷機制的具體實現:ide

  • Java中對線程中斷所提供的API支持
  • 線程在不一樣狀態下對於中斷所產生的反應

1、Java中對線程中斷所提供的API支持
     在之前的jdk版本中,咱們使用stop方法中斷線程,可是如今的jdk版本中已經再也不推薦使用該方法了,反而由如下三個方法完成對線程中斷的支持。函數

public boolean isInterrupted()

public void interrupt()

public static boolean interrupted()

每一個線程都一個狀態位用於標識當前線程對象是不是中斷狀態。isInterrupted是一個實例方法,主要用於判斷當前線程對象的中斷標誌位是否被標記了,若是被標記了則返回true表示當前已經被中斷,不然返回false。咱們也能夠看看它的實現源碼:學習

public boolean isInterrupted() {
        return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);

底層調用的本地方法isInterrupted,傳入一個boolean類型的參數,用於指定調用該方法以後是否須要清除該線程對象的中斷標識位。從這裏咱們也能夠看出來,調用isInterrupted並不會清除線程對象的中斷標識位。this

interrupt是一個實例方法,該方法用於設置當前線程對象的中斷標識位。線程

interrupted是一個靜態的方法,用於返回當前線程是否被中斷。設計

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
private native boolean isInterrupted(boolean ClearInterrupted);

該方法用於判斷當前線程是否被中斷,而且該方法調用結束的時候會清空中斷標識位。下面咱們看看線程所處不一樣狀態下對於中斷操做的反應。code

2、線程在不一樣狀態下對於中斷所產生的反應
     線程一共6種狀態,分別是NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED(Thread類中有一個State枚舉類型列舉了線程的全部狀態)。下面咱們就將把線程分別置於上述的不一樣種狀態,而後看看咱們的中斷操做對它們的影響。對象

一、NEW和TERMINATED
     線程的new狀態表示還未調用start方法,還未真正啓動。線程的terminated狀態表示線程已經運行終止。這兩個狀態下調用中斷方法來中斷線程的時候,Java認爲毫無心義,因此並不會設置線程的中斷標識位,什麼事也不會發生。例如:隊列

public static void main(String[] args) throws InterruptedException {

    Thread thread = new MyThread();
    System.out.println(thread.getState());

    thread.interrupt();

    System.out.println(thread.isInterrupted());
}

輸出結果以下:圖片

run

terminated狀態:

public static void main(String[] args) throws InterruptedException {

     Thread thread = new MyThread();
     thread.start();

     thread.join();
     System.out.println(thread.getState());

     thread.interrupt();

    System.out.println(thread.isInterrupted());

}

輸出結果以下:

這裏寫圖片描述

從上述的兩個例子來看,對於處於new和terminated狀態的線程對於中斷是屏蔽的,也就是說中斷操做對這兩種狀態下的線程是無效的。

二、RUNNABLE
     若是線程處於運行狀態,那麼該線程的狀態就是RUNNABLE,可是不必定全部處於RUNNABLE狀態的線程都能得到CPU運行,在某個時間段,只能由一個線程佔用CPU,那麼其他的線程雖然狀態是RUNNABLE,可是都沒有處於運行狀態。而咱們處於RUNNABLE狀態的線程在遭遇中斷操做的時候只會設置該線程的中斷標誌位,並不會讓線程實際中斷,想要發現本線程已經被要求中斷了則須要用程序去判斷。例如:

/*先定義一個線程類*/
public class MyThread extends Thread{

    @Override
    public void run(){
        while(true){
            //do something
        }
    }
}
/*main函數啓動線程*/
public static void main(String[] args) throws InterruptedException {

    Thread thread = new MyThread();
    thread.start();

    System.out.println(thread.getState());

    thread.interrupt();
    Thread.sleep(1000);//等到thread線程被中斷以後
    System.out.println(thread.isInterrupted());

    System.out.println(thread.getState());
}

咱們定義的線程始終循環作一些事情,主線程啓動該線程並輸出該線程的狀態,而後調用中斷方法中斷該線程並再次輸出該線程的狀態。總的輸出結果以下:

這裏寫圖片描述

能夠看到在咱們啓動線程以後,線程狀態變爲RUNNABLE,中斷以後輸出中斷標誌,顯然中斷位已經被標記,可是當咱們再次輸出線程狀態的時候發現,線程仍然處於RUNNABLE狀態。很顯然,處於RUNNBALE狀態下的線程即使遇到中斷操做,也只會設置中斷標誌位並不會實際中斷線程運行。那麼問題是,既然不能直接中斷線程,我要中斷標誌有何用處?
這裏其實Java將這種權力交給了咱們的程序,Java給咱們提供了一箇中斷標誌位,咱們的程序能夠經過if判斷中斷標誌位是否被設置來中斷咱們的程序而不是系統強制的中斷。例如:

/*修改MyThread類的run方法*/
public void run(){
    while(true){
        if (Thread.currentThread().isInterrupted()){
            System.out.println("exit MyThread");
            break;
        }
    }
}

線程一旦發現本身的中斷標誌爲被設置了,立馬跳出死循環。這樣的設計好處就在於給了咱們程序更大的靈活性。

三、BLOCKED
     當線程處於BLOCKED狀態說明該線程因爲競爭某個對象的鎖失敗而被掛在了該對象的阻塞隊列上了。那麼此時發起中斷操做不會對該線程產生任何影響,依然只是設置中斷標誌位。例如:

/*自定義線程類*/
public class MyThread extends Thread{

    public synchronized static void doSomething(){
        while(true){
            //do something
        }
    }
    @Override
    public void run(){
        doSomething();
    }
}

這裏咱們自定義了一個線程類,run方法中主要就作一件事情,調用一個有鎖的靜態方法,該方法內部是一個死循環(佔用該鎖讓其餘線程阻塞)。

public static void main(String[] args) throws InterruptedException {

    Thread thread1 = new MyThread();
    thread1.start();

    Thread thread2 = new MyThread();
    thread2.start();

    Thread.sleep(1000);
    System.out.println(thread1.getState());
    System.out.println(thread2.getState());

    thread2.interrupt();
    System.out.println(thread2.isInterrupted());
    System.out.println(thread2.getState());
}

在咱們的主線程中,咱們定義了兩個線程並按照定義順序啓動他們,顯然thread1啓動後便佔用MyThread類鎖,此後thread2在獲取鎖的時候必定失敗,天然被阻塞在阻塞隊列上,而咱們對thread2進行中斷,輸出結果以下:

這裏寫圖片描述

從輸出結果看來,thread2處於BLOCKED狀態,執行中斷操做以後,該線程仍然處於BLOCKED狀態,可是中斷標誌位卻已被修改。這種狀態下的線程和處於RUNNABLE狀態下的線程是相似的,給了咱們程序更大的靈活性去判斷和處理中斷。

四、WAITING/TIMED_WAITING
     這兩種狀態本質上是同一種狀態,只不過TIMED_WAITING在等待一段時間後會自動釋放本身,而WAITING則是無限期等待,須要其餘線程調用notify方法釋放本身。可是他們都是線程在運行的過程當中因爲缺乏某些條件而被掛起在某個對象的等待隊列上。當這些線程遇到中斷操做的時候,會拋出一個InterruptedException異常,並清空中斷標誌位。例如:

/*定義一個線程類*/
public class MyThread extends Thread{

    @Override
    public void run(){
        synchronized (this){
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("i am waiting but facing interruptexception now");
            }
        }
    }
}

咱們定義了一個線程類,其中run方法讓當前線程阻塞到條件隊列上,而且針對InterruptedException 進行捕獲,若是遇到InterruptedException 異常則輸出一行信息。

/*main函數啓動該線程*/
public static void main(String[] args) throws InterruptedException {

    Thread thread = new MyThread();
    thread.start();

    Thread.sleep(500);
    System.out.println(thread.getState());
    thread.interrupt();
    Thread.sleep(1000);
    System.out.println(thread.isInterrupted());
}

在main線程中咱們啓動一個MyThread線程,而後對其進行中斷操做。

這裏寫圖片描述

從運行結果看,當前程thread啓動以後就被掛起到該線程對象的條件隊列上,而後咱們調用interrupt方法對該線程進行中斷,輸出了咱們在catch中的輸出語句,顯然是捕獲了InterruptedException異常,接着就看到該線程的中斷標誌位被清空。

綜上所述,咱們分別介紹了不一樣種線程的不一樣狀態下對於中斷請求的反應。NEW和TERMINATED對於中斷操做幾乎是屏蔽的,RUNNABLE和BLOCKED相似,對於中斷操做只是設置中斷標誌位並無強制終止線程,對於線程的終止權利依然在程序手中。WAITING/TIMED_WAITING狀態下的線程對於中斷操做是敏感的,他們會拋出異常並清空中斷標誌位。

相關文章
相關標籤/搜索