InterruptedException異常
在了解InterruptedException異常之前應該了解以下的幾個關(guān)于線程的一些基礎(chǔ)知識蝗蛙。而且得知道什么時候會拋InterruptedException異常
當阻塞方法收到中斷請求的時候就會拋出InterruptedException異常
線程的狀態(tài)
線程在一定的條件下會發(fā)生狀態(tài)的改變,下面是線程的一些狀態(tài)
- 初始(NEW):新建一個線程的對象,還未調(diào)用start方法
- 運行(RUNNABLE):java線程中將已經(jīng)準備就緒(Ready)和正在運行中(Running)的兩種狀態(tài)都統(tǒng)稱為“Runnable”。準備就緒的線程會被放在線程池中等待被調(diào)用
- 阻塞(BLOCKED):是因為某種的原因而放棄了CPU的使用權(quán),暫時的停止了運行片酝。直到線程進入準備就緒(Ready)狀態(tài)才會有機會轉(zhuǎn)到運行狀態(tài)
- 等待(WAITING):該狀態(tài)的線程需要等待其他線程做出一些特定的動作(通知或者是中斷)
- 超時等待(TIME_WAITING):該狀態(tài)和上面的等待不同,他可以在指定的時間內(nèi)自行返回
- 終止(TERMINATED):線程任務執(zhí)行完畢
而InterruptedException異常從字面意思上就是中斷異常挖腰,那么什么是中斷呢雕沿?學習中斷之前我們先了解一下具體什么是阻塞
線程阻塞
線程阻塞通常是指一個線程在執(zhí)行過程中暫停,以等待某個條件的觸發(fā)猴仑。而什么情況才會使得線程進入阻塞的狀態(tài)呢审轮?
- 等待阻塞:運行的線程執(zhí)行wait()方法,該線程會釋放占用的所有資源辽俗,JVM會把該線程放入“等待池”中疾渣。進入這個狀態(tài)后,是不能自動喚醒的崖飘,必須依靠其他線程調(diào)用notify()或notifyAll()方法才能被喚醒
- 同步阻塞:運行的線程在獲取對象的同步鎖時榴捡,若該同步鎖被別的線程占用,則JVM會把該線程放入“鎖池”中
- 其他阻塞:運行的線程執(zhí)行sleep()或join()方法朱浴,或者發(fā)出了I/O請求時吊圾,JVM會把該線程置為阻塞狀態(tài)。當sleep()狀態(tài)超時翰蠢、join()等待線程終止或者超時项乒、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)
線程中斷
如果我們有一個運行中的軟件梁沧,例如是殺毒軟件正在全盤查殺病毒板丽,此時我們不想讓他殺毒,這時候點擊取消,那么就是正在中斷一個運行的線程埃碱。
每一個線程都有一個boolean類型的標志猖辫,此標志意思是當前的請求是否請求中斷,默認為false砚殿。當一個線程A調(diào)用了線程B的interrupt方法時啃憎,那么線程B的是否請求的中斷標志變?yōu)閠rue。而線程B可以調(diào)用方法檢測到此標志的變化似炎。
- 阻塞方法:如果線程B調(diào)用了阻塞方法辛萍,如果是否請求中斷標志變?yōu)榱藅rue,那么它會拋出InterruptedException異常羡藐。拋出異常的同時它會將線程B的是否請求中斷標志置為false
- 非阻塞方法:可以通過線程B的isInterrupted方法進行檢測是否請求中斷標志為true還是false贩毕,另外還有一個靜態(tài)的方法interrupted方法也可以檢測標志。但是靜態(tài)方法它檢測完以后會自動的將是否請求中斷標志位置為false仆嗦。例如線程A調(diào)用了線程B的interrupt的方法辉阶,那么如果此時線程B中用靜態(tài)interrupted方法進行檢測標志位的變化的話,那么第一次為true瘩扼,第二次就為false谆甜。下面為具體的例子:
/**
* @program: Test
* @description:
* @author: hu_pf@suixingpay.com
* @create: 2018-07-31 15:43
**/
public class InterrupTest implements Runnable{
public void run(){
try {
while (true) {
Boolean a = Thread.currentThread().isInterrupted();
System.out.println("in run() - about to sleep for 20 seconds-------" + a);
Thread.sleep(20000);
System.out.println("in run() - woke up");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();//如果不加上這一句,那么cd將會都是false集绰,因為在捕捉到InterruptedException異常的時候就會自動的中斷標志置為了false
Boolean c=Thread.interrupted();
Boolean d=Thread.interrupted();
System.out.println("c="+c);
System.out.println("d="+d);
}
}
public static void main(String[] args) {
InterrupTest si = new InterrupTest();
Thread t = new Thread(si);
t.start();
//主線程休眠2秒规辱,從而確保剛才啟動的線程有機會執(zhí)行一段時間
try {
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("in main() - interrupting other thread");
//中斷線程t
t.interrupt();
System.out.println("in main() - leaving");
}
}
打印的參數(shù)如下:
in run() - about to sleep for 20 seconds-------false
in main() - interrupting other thread
in main() - leaving
c=true
d=false
現(xiàn)在知道線程可以檢測到自身的標志位的變化,但是他只是一個標志栽燕,如果線程本身不處理的話罕袋,那么程序還是會執(zhí)行下去,就好比碍岔,老師在學校叮囑要好好學習浴讯,具體什么時候,如何好好學習還是看自身付秕。
因此interrupt() 方法并不能立即中斷線程兰珍,該方法僅僅告訴線程外部已經(jīng)有中斷請求侍郭,至于是否中斷還取決于線程自己
InterruptedException異常的處理
簡單的了解了什么是阻塞和中斷以后询吴,我們就該了解碰到InterruptedException異常該如何處理了。
不要不管不顧
有時候阻塞的方法拋出InterruptedException異常并不合適亮元,例如在Runnable中調(diào)用了可中斷的方法猛计,因為你的程序是實現(xiàn)了Runnable接口,然后在重寫Runnable接口的run方法的時候爆捞,那么子類拋出的異常要小于等于父類的異常奉瘤。而在Runnable中run方法是沒有拋異常的。所以此時是不能拋出InterruptedException異常。如果此時你只是記錄日志的話盗温,那么就是一個不負責任的做法藕赞,因為在捕獲InterruptedException異常的時候自動的將是否請求中斷標志置為了false。至少在捕獲了InterruptedException異常之后卖局,如果你什么也不想做斧蜕,那么就將標志重新置為true,以便棧中更高層的代碼能知道中斷砚偶,并且對中斷作出響應批销。
捕獲到InterruptedException異常后恢復中斷狀態(tài)
public class TaskRunner implements Runnable {
private BlockingQueue<Task> queue;
public TaskRunner(BlockingQueue<Task> queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
}
catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
}