前言
在上篇文章Java并發(fā)編程之線程篇之線程簡介(二)中我們基本了解了如何創(chuàng)建一個(gè)線程并執(zhí)行相應(yīng)任務(wù)表窘,但是并沒有提到如何中斷一個(gè)線程扳碍。例如:我們有一個(gè)下載程序線程,該線程在沒有下載成功之前是不會(huì)退出的,假如這個(gè)時(shí)候用戶不想下載了领突,那我們該如何中斷這個(gè)下載線程呢?下面我們就來學(xué)習(xí)如何正確的中斷一個(gè)線程吧案怯。
對于過時(shí)的suspend()君旦、resume()和stop()方法,這里就不介紹了嘲碱,有興趣的小伙伴可以查閱相關(guān)資料金砍。
Java線程的中斷機(jī)制
當(dāng)我們需要中斷某個(gè)線程時(shí),看似我們只需要調(diào)一個(gè)中斷方法(調(diào)用之后線程就不執(zhí)行了)就行了麦锯。但是Java中并沒有提供一個(gè)實(shí)際的方法來中斷某個(gè)線程(不考慮過時(shí)的stop()方法)恕稠,只提供了一個(gè)中斷標(biāo)志位,來表示線程在運(yùn)行期間已經(jīng)被其他線程進(jìn)行了中斷操作离咐。也就是說線程只有通過自身來檢查這個(gè)標(biāo)志位谱俭,來判斷自己是否被中斷了。在Java中提供了三個(gè)方法來設(shè)置或判斷中斷標(biāo)志位宵蛀,具體方法如下所示:
//類方法昆著,設(shè)置當(dāng)前線程中標(biāo)志位
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 設(shè)置中斷標(biāo)志位
b.interrupt(this);
return;
}
}
interrupt0();//設(shè)置中斷標(biāo)志位
}
//靜態(tài)方法,判斷當(dāng)前線程是否中斷术陶,清除中斷標(biāo)志位
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
//類方法凑懂,判斷當(dāng)前線程是否中斷,不清除中斷標(biāo)志位
public boolean isInterrupted() {
return isInterrupted(false);
}
在上述方法中梧宫,我們可以通過interrupt()
來設(shè)置相應(yīng)線程中斷標(biāo)志接谨,通過Thread類靜態(tài)方法interrupted()
和類方法isInterrupted()
來判斷對應(yīng)線程是否被其他線程中斷。其中interrupted()與isInterrupted()方法的主要區(qū)別如下:
- interrupted 判斷當(dāng)前線程是否中斷(如果是中斷塘匣,則會(huì)清除中斷的狀態(tài)標(biāo)志脓豪,也就是如果中斷了線程,第一次調(diào)用這個(gè)方法返回true忌卤,第二次繼續(xù)調(diào)用則返回false扫夜。
- isInterrupted 判斷線程是否已經(jīng)中斷(不清除中斷的狀態(tài)標(biāo)志)。
使用interrupt()中斷線程
在上文中驰徊,我們了解了線程的如何設(shè)置中斷標(biāo)志位與如何判斷標(biāo)志位笤闯,那現(xiàn)在我們來使用interrupt()
方法來中斷一個(gè)線程。先看下面這個(gè)例子棍厂。
class InterruptDemo {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
System.out.println("i=" + i);
}
}
});
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//輸出結(jié)果:
i=593210
i=593211
i=593212
i=593213
i=593214
i=593215
i=593216
運(yùn)行上述代碼颗味,觀察輸出結(jié)果,我們發(fā)現(xiàn)線程并沒有被終止牺弹,原因是因?yàn)?code>interrupt()方法只會(huì)設(shè)置線程中斷標(biāo)志位浦马,并不會(huì)真正的中斷線程时呀。也就是說我們只有自己來判斷線程是否終止。一般情況下捐韩,當(dāng)我們檢查到線程被中斷(也就是線程標(biāo)志位為true)時(shí)退唠,會(huì)拋出一個(gè)InterruptedException
異常,來中斷線程任務(wù)荤胁。具體代碼如下:
class InterruptDemo {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 1000000; i++) {
if (Thread.interrupted()) {
System.out.println("檢測到線程被中斷");
throw new InterruptedException();
}
System.out.println("i=" + i);
}
} catch (InterruptedException e) {
//執(zhí)行你自己的中斷邏輯
System.out.println("線程被中斷了瞧预,你自己判斷該如何處理吧");
e.printStackTrace();
}
}
});
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//輸出結(jié)果:
i=218626
i=218627
i=218628
i=218629
i=218630
檢測到線程被中斷
線程被中斷了,你自己判斷該如何處理吧
java.lang.InterruptedException
at InterruptDemo$1.run(InterruptDemo.java:18)
at java.base/java.lang.Thread.run(Thread.java:835)
在上述代碼中仅政,我們通過在線程中判斷Thread.interrupted()
來判斷線程是否中斷垢油,當(dāng)線程被中斷后,我們拋出InterruptedException異常圆丹。然后通過try/catch來捕獲該異常來執(zhí)行我們自己的中斷邏輯滩愁。當(dāng)然我們也可以通過Thread.currentThread().isInterrupted()
來判斷。這兩個(gè)方法的區(qū)別已經(jīng)在上文介紹了辫封,這里就不過多的介紹了硝枉。
中斷線程的另一種方式
在上文中提到的使用interrupt()來中斷線程以外,我們還可以通過一個(gè)boolean來控制是否中斷線程倦微。具體例子如下所示:
class InterruptDemo {
public static void main(String[] args) {
RunnableA runnableA = new RunnableA();
Thread thread = new Thread(runnableA);
thread.start();
try {
Thread.sleep(2000);
runnableA.interruptThread();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class RunnableA implements Runnable {
boolean isInterrupt;
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
if (!isInterrupt) {
System.out.println("i=" + i);
} else {
System.out.println("線程結(jié)束運(yùn)行了");
break;
}
}
}
void interruptThread() {
isInterrupt = true;
}
}
}
//輸出結(jié)果:
i=240399
i=240400
i=240401
i=240402
i=240403
i=240404
i=240405
線程結(jié)束運(yùn)行了
上述代碼中妻味,我們通過判斷isInterrupt
的值來判斷是否跳出循環(huán)。run()方法的結(jié)束欣福,就標(biāo)志著線程已經(jīng)執(zhí)行完畢了责球。
最后
站在巨人的肩膀上,才能看的更遠(yuǎn)~
- 《Java并發(fā)編程的藝術(shù)》