Java中如何正確地中斷一個線程?

本文主要整理了關于線程中斷的相關知識點。

1.線程的狀態

  • NEW (新建)
    一個尚未啟動的線程處于這一狀態。(A thread that has not yet started is in this state.)
  • RUNNABLE (可運行)
    一個正在 Java 虛擬機中執行的線程處于這一狀態。(A thread executing in the Java virtual machine is in this state.)
  • BLOCKED (阻塞)
    一個正在阻塞等待一個監視器鎖的線程處于這一狀態。(A thread that is blocked waiting for a monitor lock is in this state.)
  • WAITING (等待)
    一個正在無限期等待另一個線程執行一個特別的動作的線程處于這一狀態。(A thread that is waiting indefinitely for another thread to perform a particular action is in this state.)
  • TIMED_WAITING (計時等待)
    一個正在限時等待另一個線程執行一個動作的線程處于這一狀態。(A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.)
  • TERMINATED (終止)
    一個已經退出的線程處于這一狀態。(A thread that has exited is in this state.)

線程的狀態轉換圖 來自@牛客網:

2.如何正確中斷一個線程?

2.1 利用標志位。

這種簡單地設置標志位來中斷線程的方法,其弊端是當線程被阻塞時,沒辦法讀到標志位,也中斷不了線程。

public class TestThread extends Thread {   
    private volatile boolean finished = false;    
    public void stopThread() {        
        finished = true;
    }
    @Override    
    public void run() {        
        while (!finished) {          
            // do something
        }   
    }
}

2.2 調用Thread.interrupt()

interrupt()的本質也是利用了標志位來中斷線程,它并不會真正地中斷一個線程,而是通過改變標志位,讓線程自己根據標志位和時機,靈活地決定要不要退出線程。

關于中斷線程,JDK提供了三個與之相關的方法,之前被廢棄的方法這里就不多贅述。

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

所以調用interrupt()的時候分為兩種情況:

  • 線程正常運行的情況(沒有阻塞),那么該線程的中斷標志位被設置為true,如果沒有在線程中讀取中斷狀態,退出線程里的循環的話,線程將繼續執行。
  • 線程被阻塞的情況,由于線程已經被阻塞,要中斷線程的話,就需要將線程的中斷狀態清除,同時拋出InterruptedException異常,線程才得以中斷。
2.2.2 public static boolean interrupted()
public static boolean interrupted()
測試當前線程是否已經中斷。線程的中斷狀態由該方法清除。換句話說,如果連續兩次調用該方法,則第二次調用將返回 false(在第一次調用已清除了其中斷狀態之后,且第二次調用檢驗完中斷狀態前,當前線程再次中斷的情況除外)。 

interrupted(),首先會返回當前線程的中斷狀態,然后會將線程的中斷狀態清除,也就是將標志位設置為false。

2.2.3 public boolean isInterrupted()
public boolean isInterrupted()
測試線程是否已經中斷。線程的中斷狀態不受該方法的影響。 

而isInterrupted(),只會返回線程中斷狀態,不會修改標志位。

這兩者的差別 簡單地說就是:

Thread.isInterrupted() 用來讀取中斷狀態, Thread.interrupted() 用來讀取中斷狀態和清除標志位。

tips:在線程執行完畢之后,線程的中斷狀態會被修改為false。

2.2.4 例子:
public class TestThread extends Thread {
     @Override
     public void run() {
          while (!isInterrupted()) {
                try {
                   // do something
                   
                    } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
          }
     }
}

TestThread testThread = new TestThread();
testThread.start();
// 一段時間以后
testThread.interrupt();

總的來說,當新開的線程被阻塞了,在調用interrupt()的時候,線程的中斷狀態清除,同時拋出InterruptedException異常,注意,這個時候中斷狀態被清除了!你需要在catch語句里面重新調用interrupt(),來維持中斷狀態,否則,由于中斷狀態被清除,當程序繼續執行到while (!isInterrupted())的時候,線程是不會停下來的。

參考資料:

https://www.ibm.com/developerworks/cn/java/j-jtp05236.html

http://ibruce.info/2013/12/19/how-to-stop-a-java-thread/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容