本文主要整理了關(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ì)停下來的。
參考資料: