Java中如何正確地中斷一個(gè)線程?

本文主要整理了關(guān)于線程中斷的相關(guān)知識(shí)點(diǎn)。

1.線程的狀態(tài)

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

線程的狀態(tài)轉(zhuǎn)換圖 來自@牛客網(wǎng):

2.如何正確中斷一個(gè)線程?

2.1 利用標(biāo)志位。

這種簡單地設(shè)置標(biāo)志位來中斷線程的方法,其弊端是當(dāng)線程被阻塞時(shí),沒辦法讀到標(biāo)志位,也中斷不了線程。

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 調(diào)用Thread.interrupt()

interrupt()的本質(zhì)也是利用了標(biāo)志位來中斷線程,它并不會(huì)真正地中斷一個(gè)線程,而是通過改變標(biāo)志位,讓線程自己根據(jù)標(biāo)志位和時(shí)機(jī),靈活地決定要不要退出線程

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

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

所以調(diào)用interrupt()的時(shí)候分為兩種情況:

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

interrupted(),首先會(huì)返回當(dāng)前線程的中斷狀態(tài),然后會(huì)將線程的中斷狀態(tài)清除,也就是將標(biāo)志位設(shè)置為false。

2.2.3 public boolean isInterrupted()
public boolean isInterrupted()
測(cè)試線程是否已經(jīng)中斷。線程的中斷狀態(tài)不受該方法的影響。 

而isInterrupted(),只會(huì)返回線程中斷狀態(tài),不會(huì)修改標(biāo)志位。

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

Thread.isInterrupted() 用來讀取中斷狀態(tài), Thread.interrupted() 用來讀取中斷狀態(tài)和清除標(biāo)志位。

tips:在線程執(zhí)行完畢之后,線程的中斷狀態(tài)會(huì)被修改為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();
// 一段時(shí)間以后
testThread.interrupt();

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

參考資料:

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容