本文由淺入深介紹了中斷機(jī)制、中斷的常見案例和使用場景。
1. 為什么需要
因?yàn)橐恍┰蛐枰∠菊趫?zhí)行的線程。我們舉幾個(gè)栗子:
- 假設(shè)踢足球點(diǎn)球時(shí),A隊(duì)前4輪中了4個(gè)球,B隊(duì)前4輪只中了2個(gè)球,此時(shí)勝負(fù)已分,第5輪這個(gè)點(diǎn)球就不用踢了,此時(shí)需要停止A隊(duì)的線程和B隊(duì)的線程(共5個(gè)點(diǎn)球)。
- 假設(shè)用戶從網(wǎng)絡(luò)上下載一個(gè)50M的文件,如果網(wǎng)絡(luò)很慢,用戶等的不耐煩,點(diǎn)了取消下載操作,此時(shí)需要停止下載的線程。
2. 如何理解
中斷可以理解為線程的一個(gè)內(nèi)部標(biāo)識(shí)位屬性,它表示一個(gè)運(yùn)行中的線程是否被其他線程進(jìn)行了中斷操作。
中斷就是其他線程通過interrupt
方法給該線程發(fā)一個(gè)信號(hào),該線程收到信號(hào)后可以選擇響應(yīng)或不響應(yīng)信號(hào),中斷它只是一種協(xié)商機(jī)制。響應(yīng)信號(hào)通常會(huì)結(jié)束自身run()
方法的執(zhí)行退出線程,不響應(yīng)則繼續(xù)執(zhí)行無任何影響。
線程通過檢測自身是否被中斷來進(jìn)行響應(yīng),檢測方法有2種,分別是public static boolean interrupted()
和public boolean isInterrupted()
,靜態(tài)的方法會(huì)清除中斷標(biāo)識(shí)位屬性,非靜態(tài)方法不會(huì)清除中斷標(biāo)識(shí)位屬性。
3. 如何使用
3.1. 中斷相關(guān)API
Thread源碼中關(guān)于中斷的方法:
public class Thread implements Runnable {
// 僅僅只是設(shè)置中斷狀態(tài)
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
// 會(huì)清除中斷狀態(tài)
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 不會(huì)清除中斷狀態(tài)
public boolean isInterrupted() {
return isInterrupted(false);
}
private native void interrupt0();
private native boolean isInterrupted(boolean ClearInterrupted);
}
繪制成表格:
API | 作用 |
---|---|
public void interrupt() | 中斷this線程 |
public static boolean interrupted() | 測試當(dāng)前線程是否被中斷,且線程的中斷狀態(tài)會(huì)被清除 |
public boolean isInterrupted() | 測試this線程是否中斷。 中斷狀態(tài)不會(huì)被這個(gè)方法影響 |
特別需要注意的是:靜態(tài)interrupted方法會(huì)清除線程的中斷狀態(tài)。
3.2. 中斷正常狀態(tài)下的線程
中斷一個(gè)線程非常簡單,只需要在其他線程中對(duì)目標(biāo)線程調(diào)用interrupt()
方法,目標(biāo)線程需要反復(fù)檢測自身狀態(tài)是否是interrupted狀態(tài),如果是,就立刻結(jié)束運(yùn)行。
public class Main extends Thread {
@Override
public void run() {
int i = 0;
while (!Thread.currentThread().isInterrupted()) {
i++;
System.out.println();
}
System.out.println("done");
}
public static void main(String[] args) throws InterruptedException {
Thread t = new Main();
t.start();
t.sleep(1000);
t.interrupt();
}
}
main
線程通過調(diào)用t.interrupt()
中斷t
線程。但需要注意,interrupt()
方法僅僅是給t
線程發(fā)送了"中斷請(qǐng)求",只是把t
線程的標(biāo)識(shí)屬性設(shè)置為中斷,至于t
線程是否響應(yīng)或處理該中斷請(qǐng)求則要看t
線程的具體代碼。
這里,t
線程在while中循環(huán)檢測中斷請(qǐng)求(isInterrupted()
),如果檢測到線程被中斷則跳出while循環(huán)結(jié)束線程的run()
方法。
3.3. 中斷特殊狀態(tài)下的線程
特殊狀態(tài)其實(shí)在官網(wǎng)Thread中有好幾種,這里只列舉最常見的一種
如果線程
- 被阻塞在Object類的
wait()
,wait(long)
,wait(long, int)
方法 - 或被阻塞在Thread類的
join()
,join(long)
,join(long, int)
,sleep(long)
,sleep(long, int)
如果此時(shí)調(diào)用線程的中斷方法interrupt()
,線程的中斷狀態(tài)會(huì)被清除且拋出InterruptedException
異常。這種情況的一般處理方法是:捕獲InterruptedException
異常,然后重新設(shè)置中斷狀態(tài),即調(diào)用Thread.currentThread().interrupt();
我們來看示例代碼:
public class Main extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
// 模擬任務(wù)代碼
Thread.sleep(2000);
} catch (InterruptedException e) {
// ... 清理操作
System.out.println(isInterrupted());//false
// 重設(shè)中斷標(biāo)志位為true
Thread.currentThread().interrupt();
}
}
System.out.println(isInterrupted());//true
}
public static void main(String[] args) {
Main t = new Main();
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
t.interrupt();
}
}
4. 如何安全的停止線程
前面提到中斷狀態(tài)只是線程的一個(gè)標(biāo)識(shí)位,而中斷操作是一種簡便的線程間交互方式,而這種交互方式最適合用來取消或停止任務(wù)。除了中斷以外,還可以利用一個(gè)boolean變量來控制是否需要停止任務(wù)并終止該線程。 一般的,可以用以下方式來安全的停止線程:
- 中斷方式
- volatile修飾的boolean變量方式
代碼舉例如下:
public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread t = new Thread(one, "t");
t.start();
// 睡眠1秒,讓Runner線程充分運(yùn)行,隨后能夠感知中斷而結(jié)束
TimeUnit.SECONDS.sleep(1);
t.interrupt();
Runner two = new Runner();
t = new Thread(two, "t");
t.start();
// 睡眠1秒,讓Runner線程充分運(yùn)行,隨后能夠感知on為false而結(jié)束
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}
5. 參考資料
《Java并發(fā)編程的藝術(shù)》,可聯(lián)系作者無套路下載可復(fù)制的電子版pdf書籍文章來源:http://www.zghlxwxcb.cn/news/detail-643031.html
官方Thread類的源碼和注釋文章來源地址http://www.zghlxwxcb.cn/news/detail-643031.html
到了這里,關(guān)于【Java并發(fā)編程】線程中斷機(jī)制(輔以常見案例)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!