Java執行緒Thread之interrupt中斷解析
轉載請標明出處:
http://blog.csdn.net/hesong1120/article/details/79164445
本文出自:hesong的專欄
這一篇我們說說Java執行緒Thread的interrupt中斷機制。
interrupt之中斷狀態標記
interrupt中斷機制中有如下方法:
- Thread.interrupt(),設定當前中斷標記為true(類似屬性的set方法)
- Thread.isInterrupted(),檢測當前的中斷標記(類似屬性的get方法)
- Thread.interrupted(),檢測當前的中斷標記,然後重置中斷標記為false(類似屬性的get方法+set方法)
因此interrupt中斷機制並不是真正的將當前執行緒中斷,而是一箇中斷標記的變化。我們先用例子來測試一下。
public class InterruptTest {
//這裡用來列印消耗的時間
private static long time = 0;
private static void resetTime(){
time = System.currentTimeMillis();
}
private static void printContent(String content){
System.out.println(content + " 時間:" + (System.currentTimeMillis() - time));
}
public static void main(String[] args) {
test1();
}
private static void test1(){
Thread1 thread1 = new Thread1();
thread1.start();
//延時3秒後interrupt中斷
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
printContent("執行中斷" );
}
private static class Thread1 extends Thread{
@Override public void run() {
resetTime();
int num = 0;
while (true){
if(isInterrupted()){
printContent("當前執行緒 isInterrupted");
break;
}
num++;
if(num % 100 == 0){
printContent("num : " + num);
}
}
}
}
}
以上程式碼是開啟一個Thread1執行緒,在Thread1執行緒的while迴圈中不斷對num加1,每到100的倍數列印一次(防止列印太快)。然後主執行緒在sleep了3000毫秒後,呼叫Thread1執行緒的interrupt方法。那麼我們看看輸出結果:
可以看到,在耗時3000毫秒左右,也就是主執行緒sleep之後執行thread1.interrupt();後,Thread1執行緒停止了,而Thread1執行緒的停止是因為while迴圈中的isInterrupted方法返回了true,所以break退出了while迴圈,也就是說interrupt和isInterrupted在這裡起到的作用就相當於setXX和getXX的作用,維護著一個boolean變數。
interrupt之中斷異常處理
當然interrupt機制並不僅僅是一箇中斷狀態位的變化和檢測,它還可以進行中斷異常的處理。我們知道Thread.sleep()方法需要捕獲中斷異常,那接下來我們往其中新增一個sleep延時試試
while (true){
if(isInterrupted()){
printContent("當前執行緒 isInterrupted");
break;
}
num++;
//sleep一下
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num % 100 == 0){
printContent("num : " + num);
}
}
我們再看看輸出結果:
這裡我們會發現,sleep睡眠之後,輸出的num值明顯小了好多(沒睡眠時num都達到10億的大小了,看來CPU執行簡單運算還是非常快的),哈哈,不過這不是重點,重點是是看到輸出了一個異常,還有就是輸出異常後,isInterrupted輸出返回false,Thread1執行緒又繼續執行下去了,並沒有退出while迴圈。那麼這是為什麼呢?我們只是加了一個sleep睡眠而已。
如果Thread1執行緒中有執行需要捕獲InterruptedException異常的操作,比如Thread的sleep,join方法,Object的wait,Condition的await等,它是強制需要捕獲InterruptedException異常的,那麼當thread1.interrupt方法呼叫之後,它會給thread1執行緒丟擲一個InterruptedException異常,那麼在while迴圈中,就能捕獲到這個異常然後這個異常丟擲之後,又會馬上將執行緒中斷標識重置為false,因此在下次的while迴圈中判斷isInterrupted時,它是false,也就不會break,然後while迴圈會一直執行下去。
因此interrupt()方法會根據thread執行緒中的run方法裡是否有必須捕獲InterruptedException異常的程式碼,而做出不同操作:
- 如果沒有必須捕獲InterruptedException異常的程式碼(比如Thread.sleep()),則isInterrupted()會返回true,此時可以在isInterrupted的判斷中處理中斷變化。
- 如果有必須捕獲InterruptedException異常的程式碼(比如Thread.sleep()),則會丟擲InterruptedException異常,並進行捕獲,同時重置isInterrupted為false,此時得在異常捕獲中處理中斷變化。
interrupt的應用場景
通常interrupt適用於線上程執行中的迴圈標記判斷,例如
while(!isInterrupted()){
...
}
但是如果在本次迴圈中出現阻塞了,那麼執行緒就無法判斷下次的isInterrupted標記,那麼即便呼叫了interrupt()方法也無法退出迴圈,也就無法退出執行緒。例如
}
while(!isInterrupted()){
...
while(true){
//執行緒卡在這裡了,則無法響應interrupte機制了
}
}
這樣的話,interrupt就沒轍了,執行緒會一直執行下去,不會被中斷停止。
測試例子檢視 我的GitHub–JavaTest
我的部落格
GitHub
我的簡書
群號:194118438,歡迎入群
微信公眾號 hesong ,微信掃一掃下方二維碼即可關注: