前言
在上篇文章Java併發程式設計之執行緒篇之執行緒簡介(二)中我們基本瞭解瞭如何建立一個執行緒並執行相應任務,但是並沒有提到如何中斷一個執行緒。例如:我們有一個下載程式執行緒,該執行緒在沒有下載成功之前是不會退出的,假如這個時候使用者不想下載了,那我們該如何中斷這個下載執行緒呢?下面我們就來學習如何正確的中斷一個執行緒吧。
對於過時的suspend()、resume()和stop()方法,這裡就不介紹了,有興趣的小夥伴可以查閱相關資料。
Java執行緒的中斷機制
當我們需要中斷某個執行緒時,看似我們只需要調一箇中斷方法(呼叫之後執行緒就不執行了)就行了。但是Java中並沒有提供一個實際的方法來中斷某個執行緒(不考慮過時的stop()方法),只提供了一箇中斷標誌位,來表示執行緒在執行期間已經被其他執行緒進行了中斷操作。也就是說執行緒只有通過自身來檢查這個標誌位,來判斷自己是否被中斷了。在Java中提供了三個方法來設定或判斷中斷標誌位,具體方法如下所示:
//類方法,設定當前執行緒中標誌位
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 設定中斷標誌位
b.interrupt(this);
return;
}
}
interrupt0();//設定中斷標誌位
}
//靜態方法,判斷當前執行緒是否中斷,清除中斷標誌位
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
//類方法,判斷當前執行緒是否中斷,不清除中斷標誌位
public boolean isInterrupted() {
return isInterrupted(false);
}
複製程式碼
在上述方法中,我們可以通過interrupt()
來設定相應執行緒中斷標誌,通過Thread類靜態方法interrupted()
和類方法isInterrupted()
來判斷對應執行緒是否被其他執行緒中斷。
其中interrupted()與isInterrupted()方法的主要區別如下:
interrupted
判斷當前執行緒是否中斷(如果是中斷,則會清除中斷的狀態標誌,也就是如果中斷了執行緒,第一次呼叫這個方法返回true,第二次繼續呼叫則返回false。isInterrupted
判斷執行緒是否已經中斷(不清除中斷的狀態標誌)。
使用interrupt()中斷執行緒
在上文中,我們瞭解了執行緒的如何設定中斷標誌位與如何判斷標誌位,那現在我們來使用interrupt()
方法來中斷一個執行緒。先看下面這個例子。
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();
}
}
}
//輸出結果:
i=593210
i=593211
i=593212
i=593213
i=593214
i=593215
i=593216
複製程式碼
執行上述程式碼,觀察輸出結果,我們發現執行緒並沒有被終止,原因是因為interrupt()方法只會設定執行緒中斷標誌位,並不會真正的中斷執行緒
。也就是說我們只有自己來判斷執行緒是否終止。一般情況下,當我們檢查到執行緒被中斷(也就是執行緒標誌位為true)時,會丟擲一個InterruptedException
異常,來中斷執行緒任務。具體程式碼如下:
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) {
//執行你自己的中斷邏輯
System.out.println("執行緒被中斷了,你自己判斷該如何處理吧");
e.printStackTrace();
}
}
});
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//輸出結果:
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()
來判斷執行緒是否中斷,當執行緒被中斷後,我們丟擲InterruptedException異常。然後通過try/catch來捕獲該異常來執行我們自己的中斷邏輯。當然我們也可以通過Thread.currentThread().isInterrupted()
來判斷。這兩個方法的區別已經在上文介紹了,這裡就不過多的介紹了。
中斷執行緒的另一種方式
在上文中提到的使用interrupt()來中斷執行緒以外,我們還可以通過一個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("執行緒結束執行了");
break;
}
}
}
void interruptThread() {
isInterrupt = true;
}
}
}
//輸出結果:
i=240399
i=240400
i=240401
i=240402
i=240403
i=240404
i=240405
執行緒結束執行了
複製程式碼
上述程式碼中,我們通過判斷isInterrupt
的值來判斷是否跳出迴圈。run()方法的結束,就標誌著執行緒已經執行完畢了。
最後
站在巨人的肩膀上,才能看的更遠~
- 《Java併發程式設計的藝術》