正如中斷二字所表達的意義,在線程運行(run方法)中間打斷它,在Java中,提供了以下3個有關線程中斷的方法
//中斷線程(實例方法)
public void Thread.interrupt();
//判斷線程是否被中斷(實例方法)
public boolean Thread.isInterrupted();
//判斷是否被中斷並清除當前中斷狀態(靜態方法)
public static boolean Thread.interrupted();
當一個線程處於被阻塞狀態或者試圖執行一個阻塞操作時,使用Thread.interrupt()
方式中斷該線程,注意此時將會拋出一個InterruptedException的異常,同時中斷狀態將會被複位(由中斷狀態改爲非中斷狀態),如下代碼將演示該過程:
package com.lxk.thread.Interrupt;
import java.util.concurrent.TimeUnit;
/**
* 阻塞中斷
* <p>
* 當一個線程處於被阻塞狀態或者試圖執行一個阻塞操作時,使用Thread.interrupt()方式中斷該線程,
* 注意此時將會拋出一個InterruptedException的異常,同時中斷狀態將會被複位(由中斷狀態改爲非中斷狀態)
*
* @author LiXuekai on 2020/4/30
*/
public class InterruptThread1 {
/**
* 阻塞中斷
*/
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
//while在try中,通過異常中斷就可以退出run循環
try {
while (true) {
//當前線程處於阻塞狀態,異常必須捕捉處理,無法往外拋出
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
System.out.println("interrupted When Sleep");
boolean interrupt = this.isInterrupted();
//中斷狀態被複位
System.out.println("interrupt:" + interrupt);
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
//中斷處於阻塞狀態的線程
t1.interrupt();
/*
* 輸出結果:
interrupted When Sleep
interrupt:false
*/
}
}
如上述代碼所示,我們創建一個線程,並在線程中調用了sleep方法從而使用線程進入阻塞狀態,啓動線程後,調用線程實例對象的interrupt方法中斷阻塞異常,並拋出InterruptedException異常,此時中斷狀態也將被複位。這裏有些人可能會詫異,爲什麼不用Thread.sleep(2000);而是用TimeUnit.SECONDS.sleep(2);其實原因很簡單,前者使用時並沒有明確的單位說明,而後者非常明確表達秒的單位,事實上後者的內部實現最終還是調用了Thread.sleep(2000);,但爲了編寫的代碼語義更清晰,建議使用TimeUnit.SECONDS.sleep(2);的方式,注意TimeUnit是個枚舉類型。ok~,除了阻塞中斷的情景,我們還可能會遇到處於運行期且非阻塞的狀態的線程,這種情況下,直接調用Thread.interrupt()中斷線程是不會得到任響應的,如下代碼,將無法中斷非阻塞狀態下的線程:
package com.lxk.thread.Interrupt;
import java.util.concurrent.TimeUnit;
/**
* 處於運行期且非阻塞的狀態的線程
* 直接調用Thread.interrupt()中斷線程是不會得到任響應的
*
* @author LiXuekai on 2020/4/30
*/
public class InterruptThread2 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
System.out.println("未被中斷");
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
/*
* 輸出結果(無限執行):
未被中斷
未被中斷
未被中斷
......
*/
// 雖然我們調用了interrupt方法,但線程t1並未被中斷,因爲處於非阻塞狀態的線程需要我們手動進行中斷檢測並結束程序
}
}
改進後代碼如下:
package com.lxk.thread.Interrupt;
import java.util.concurrent.TimeUnit;
/**
* 當線程處於運行狀態時,我們也可調用實例方法interrupt()進行線程中斷,
* 但同時必須手動判斷中斷狀態,並編寫中斷線程的代碼(其實就是結束run方法體的代碼)
* @author LiXuekai on 2020/4/30
*/
public class InterruptThread3 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
//判斷當前線程是否被中斷
if (this.isInterrupted()) {
System.out.println("線程中斷");
break;
}
}
System.out.println("已跳出循環,線程中斷!");
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
/*
* 輸出結果:
線程中斷
已跳出循環,線程中斷!
*/
}
}
是的,我們在代碼中使用了實例方法isInterrupted判斷線程是否已被中斷,如果被中斷將跳出循環以此結束線程,注意非阻塞狀態調用interrupt()
並不會導致中斷狀態重置。
綜合所述,可以簡單總結一下中斷兩種情況,一種是當線程處於阻塞狀態或者試圖執行一個阻塞操作時,我們可以使用實例方法interrupt()進行線程中斷,執行中斷操作後將會拋出interruptException異常(該異常必須捕捉無法向外拋出)並將中斷狀態復位,另外一種是當線程處於運行狀態時,我們也可調用實例方法interrupt()進行線程中斷,但同時必須手動判斷中斷狀態,並編寫中斷線程的代碼(其實就是結束run方法體的代碼)。有時我們在編碼時可能需要兼顧以上兩種情況,那麼就可以如下編寫:
package com.lxk.thread.Interrupt;
import java.util.concurrent.TimeUnit;
/**
* 一種是當線程處於阻塞狀態或者試圖執行一個阻塞操作時,我們可以使用實例方法interrupt()進行線程中斷,執行中斷操作後將會拋出interruptException異常(該異常必須捕捉無法向外拋出)並將中斷狀態復位,
* 另外一種是當線程處於運行狀態時,我們也可調用實例方法interrupt()進行線程中斷,但同時必須手動判斷中斷狀態,並編寫中斷線程的代碼(其實就是結束run方法體的代碼)。
* 有時我們在編碼時可能需要兼顧以上兩種情況,那麼就可以如下編寫
*
* @author LiXuekai on 2020/4/30
*/
public class InterruptThread4 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
try {
//判斷當前線程是否已中斷,注意interrupted方法是靜態的,執行後會對中斷狀態進行復位
while (!Thread.interrupted()) {
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
System.out.println(e);
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
/*
* 輸出結果:
java.lang.InterruptedException: sleep interrupted
*/
}
}
Java線程的中斷 != stop 線程