深入分析JAVA 多線程--interrupt()和線程終止方式
一、interrupt() 介紹
interrupt() 定義在 Thread 類中,作用是中斷本線程。
本線程中斷自己是被允許的;其它線程調(diào)用本線程的 interrupt() 方法時(shí),會(huì)通過(guò) checkAccess() 檢查權(quán)限。這有可能拋出 SecurityException 異常。如果本線程是處于阻塞狀態(tài):調(diào)用線程的 wait() , wait(long) 或 wait(long, int) 會(huì)讓它進(jìn)入等待(阻塞)狀態(tài),或者調(diào)用線程的 join(),join(long),join(long, int),sleep(long),sleep(long, int) 也會(huì)讓它進(jìn)入阻塞狀態(tài)。若線程在阻塞狀態(tài)時(shí),調(diào)用了它的 interrupt() 方法,那么它的“中斷狀態(tài)”會(huì)被清除并且會(huì)收到一個(gè) InterruptedException 異常。例如,線程通過(guò) wait() 進(jìn)入阻塞狀態(tài),此時(shí)通過(guò) interrupt() 中斷該線程;調(diào)用 interrupt() 會(huì)立即將線程的中斷標(biāo)記設(shè)為 true,但是由于線程處于阻塞狀態(tài),所以該“中斷標(biāo)記”會(huì)立即被清除為 “false”,同時(shí),會(huì)產(chǎn)生一個(gè) InterruptedException 的異常。如果線程被阻塞在一個(gè) Selector 選擇器中,那么通過(guò) interrupt() 中斷它時(shí);線程的中斷標(biāo)記會(huì)被設(shè)置為 true,并且它會(huì)立即從選擇操作中返回。如果不屬于前面所說(shuō)的情況,那么通過(guò) interrupt() 中斷線程時(shí),它的中斷標(biāo)記會(huì)被設(shè)置為 true。中斷一個(gè)“已終止的線程”不會(huì)產(chǎn)生任何操作。
二、線程終止方式
Thread中的 stop() 和 suspend() 方法,由于固有的不安全性,已經(jīng)建議不再使用!下面,我先分別討論線程在“阻塞狀態(tài)”和“運(yùn)行狀態(tài)”的終止方式,然后再總結(jié)出一個(gè)通用的方式。
(一)、終止處于“阻塞狀態(tài)”的線程.
通常,我們通過(guò)“中斷”方式終止處于“阻塞狀態(tài)”的線程。當(dāng)線程由于被調(diào)用了 sleep(),,wait(),join() 等方法而進(jìn)入阻塞狀態(tài);若此時(shí)調(diào)用線程的 interrupt() 將線程的中斷標(biāo)記設(shè)為 true。由于處于阻塞狀態(tài),中斷標(biāo)記會(huì)被清除,同時(shí)產(chǎn)生一個(gè)InterruptedException 異常。將 InterruptedException 放在適當(dāng)?shù)奈恢镁湍芙K止線程,形式如下:
public void run() { try { while (true) { // 執(zhí)行業(yè)務(wù) } } catch (InterruptedException ie) { // 由于產(chǎn)生InterruptedException異常,退出while(true)循環(huán),線程終止! }}
說(shuō)明:
在while(true)中不斷的執(zhí)行業(yè)務(wù)代碼,當(dāng)線程處于阻塞狀態(tài)時(shí),調(diào)用線程的 interrupt() 產(chǎn)生 InterruptedException 中斷。中斷的捕獲在 while(true) 之外,這樣就退出了 while(true) 循環(huán)!
注意:
對(duì) InterruptedException 的捕獲務(wù)一般放在 while(true) 循環(huán)體的外面,這樣,在產(chǎn)生異常時(shí)就退出了 while(true) 循環(huán)。否則,InterruptedException 在 while(true) 循環(huán)體之內(nèi),就需要額外的添加退出處理。形式如下:
public void run() { while (true) { try { // 執(zhí)行任務(wù)... } catch (InterruptedException ie) { // InterruptedException在while(true)循環(huán)體內(nèi)。 // 當(dāng)線程產(chǎn)生了InterruptedException異常時(shí),while(true)仍能繼續(xù)運(yùn)行!需要手動(dòng)退出 break; } }}
說(shuō)明:
上面的 InterruptedException 異常的捕獲在 whle(true) 之內(nèi)。當(dāng)產(chǎn)生 InterruptedException 異常時(shí),被 catch 處理之外,仍然在 while(true) 循環(huán)體內(nèi);要退出 while(true) 循環(huán)體,需要額外的執(zhí)行退出while(true) 的操作。
(二)、終止處于“運(yùn)行狀態(tài)”的線程
通常,我們通過(guò)“標(biāo)記”方式終止處于“運(yùn)行狀態(tài)”的線程。其中,包括“中斷標(biāo)記”和“額外添加標(biāo)記”。
1、通過(guò)“中斷標(biāo)記”終止線程
public void run() { while (!isInterrupted()) { // 執(zhí)行任務(wù)... }}
說(shuō)明:
isInterrupted() 是判斷線程的中斷標(biāo)記是不是為 true。當(dāng)線程處于運(yùn)行狀態(tài),并且我們需要終止它時(shí);可以調(diào)用線程的 interrupt() 方法,使用線程的中斷標(biāo)記為 true,即 isInterrupted() 會(huì)返回true。此時(shí),就會(huì)退出while循環(huán)。
注意:interrupt() 并不會(huì)終止處于“運(yùn)行狀態(tài)”的線程!它會(huì)將線程的中斷標(biāo)記設(shè)為 true。
2、通過(guò)“額外添加標(biāo)記”終止線程
private volatile boolean flag= true;protected void stopTask() { flag = false;}public void run() { while (flag) { // 執(zhí)行任務(wù)... }}
說(shuō)明:
線程中有一個(gè) flag 標(biāo)記,它的默認(rèn)值是 true;并且我們提供 stopTask() 來(lái)設(shè)置 flag 標(biāo)記。當(dāng)我們需要終止該線程時(shí),調(diào)用該線程的 stopTask() 方法就可以讓線程退出 while 循環(huán)。
注意:將 flag 定義為 volatile 類型,是為了保證 flag 的可見(jiàn)性。即其它線程通過(guò) stopTask() 修改了 flag 之后,本線程能看到修改后的 flag 的值。
(三)、通過(guò)方式
綜合線程處于“阻塞狀態(tài)”和“運(yùn)行狀態(tài)”的終止方式,比較通用的終止線程的形式如下:
public void run() { try { // 1. isInterrupted()保證,只要中斷標(biāo)記為true就終止線程。 while (!isInterrupted()) { // 執(zhí)行任務(wù)... } } catch (InterruptedException ie) { // 2. InterruptedException異常保證,當(dāng)InterruptedException異常產(chǎn)生時(shí),線程被終止。 }}
1、isInterrupted()保證,只要中斷標(biāo)記為 true 就終止線程。2、InterruptedException 異常保證,當(dāng) InterruptedException 異常產(chǎn)生時(shí),線程被終止。
三、示例
public class InterruptTest { public static void main(String[] args) { try { Thread t1 = new MyThread('t1'); // 新建線程t1 System.out.println(t1.getName() + '[' + t1.getState() + '] is new.'); t1.start();// 啟動(dòng)線程t1 System.out.println(t1.getName() + '[' + t1.getState() + '] is started.'); Thread.sleep(300);// 休眠300毫秒,然后主線程給t1發(fā)“中斷”指令,查看t1狀態(tài) t1.interrupt(); System.out.println(t1.getName() + '[' + t1.getState() + '] is interrupted.'); Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態(tài) System.out.println(t1.getName() + '[' + t1.getState() + '] is interrupted now.'); }catch(InterruptedException e) { e.printStackTrace(); } }}class MyThread extends Thread{ public MyThread(String name) { super(name); } @Override public void run() { try { int i = 0; while(!isInterrupted()) {Thread.sleep(100);// 休眠100毫秒++i;System.out.println(Thread.currentThread().getName() + '[' + this.getState() + '] loop ' + i); } }catch(InterruptedException e) { System.out.println(Thread.currentThread().getName() + '[' + this.getState() + '] catch InterruptedException'); } }}
運(yùn)行結(jié)果
t1 [ NEW ] is new.t1 [ RUNNABLE ] is started.t1 [ RUNNABLE ] loop 1t1 [ RUNNABLE ] loop 2t1 [ RUNNABLE ] loop 3t1 [ RUNNABLE ] catch InterruptedExceptiont1 [ TERMINATED ] is interrupted.t1 [ TERMINATED ] is interrupted now.
說(shuō)明:
①、主線程 main 中通過(guò) new MyThread('t1') 創(chuàng)建線程 t1,之后通過(guò) t1.start() 啟動(dòng)線程 t1。
②、t1 啟動(dòng)之后,會(huì)不斷的檢查它的中斷標(biāo)記,如果中斷標(biāo)記為“false”;則休眠 100ms。
③、t1 休眠之后,會(huì)切換到主線程main;主線程再次運(yùn)行時(shí),會(huì)執(zhí)行t1.interrupt()中斷線程t1。t1收到中斷指令之后,會(huì)將t1的中斷標(biāo)記設(shè)置“false”,而且會(huì)拋出 InterruptedException 異常。在 t1 的 run() 方法中,是在循環(huán)體 while 之外捕獲的異常;因此循環(huán)被終止。
我們對(duì)上面的結(jié)果進(jìn)行小小的修改,將run()方法中捕獲InterruptedException異常的代碼塊移到while循環(huán)體內(nèi)。
public class InterruptTest { public static void main(String[] args) { try { Thread t1 = new MyThread('t1'); // 新建線程t1 System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is new.'); t1.start();// 啟動(dòng)線程t1 System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is started.'); Thread.sleep(300);// 休眠300毫秒,然后主線程給t1發(fā)“中斷”指令,查看t1狀態(tài) t1.interrupt(); System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is interrupted.'); Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態(tài) System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is interrupted now.'); }catch(InterruptedException e) { e.printStackTrace(); } }}class MyThread extends Thread{ public MyThread(String name) { super(name); } @Override public void run() { int i = 0; while(!isInterrupted()) { try {Thread.sleep(100); // 休眠100ms } catch (InterruptedException ie) { System.out.println(Thread.currentThread().getName() +' [ '+this.getState()+' ] catch InterruptedException.'); } i++; System.out.println(Thread.currentThread().getName()+' [ '+this.getState()+' ] loop ' + i); } }}
運(yùn)行結(jié)果
t1 [ NEW ] is new.t1 [ RUNNABLE ] is started.t1 [ RUNNABLE ] loop 1t1 [ RUNNABLE ] loop 2t1 [ TIMED_WAITING ] is interrupted.t1 [ RUNNABLE ] catch InterruptedException.t1 [ RUNNABLE ] loop 3t1 [ RUNNABLE ] loop 4t1 [ RUNNABLE ] loop 5t1 [ RUNNABLE ] loop 6t1 [ RUNNABLE ] is interrupted now.t1 [ RUNNABLE ] loop 7...... // 無(wú)限循環(huán)
說(shuō)明:
程序進(jìn)入了死循環(huán)了。
這是因?yàn)椋瑃1在“等待(阻塞)狀態(tài)”時(shí),被 interrupt() 中斷;此時(shí),會(huì)清除中斷標(biāo)記(即 isInterrupted() 會(huì)返回 false),而且會(huì)拋出 InterruptedException 異常(該異常在while循環(huán)體內(nèi)被捕獲)。因此,t1理所當(dāng)然的會(huì)進(jìn)入死循環(huán)了。
解決該問(wèn)題,需要我們?cè)诓东@異常時(shí),額外的進(jìn)行退出 while 循環(huán)的處理。例如,在 MyThread 的 catch(InterruptedException) 中添加 break 或 return 就能解決該問(wèn)題。
下面是通過(guò)“額外添加標(biāo)記”的方式終止“狀態(tài)狀態(tài)”的線程的示例:
public class InterruptTest { public static void main(String[] args) { try { MyThread t1 = new MyThread('t1'); // 新建線程t1 System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is new.'); t1.start();// 啟動(dòng)線程t1 System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is started.'); Thread.sleep(300);// 休眠300毫秒,然后主線程給t1發(fā)“中斷”指令,查看t1狀態(tài) t1.stopTask(); System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is interrupted.'); Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態(tài) System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is interrupted now.'); }catch(InterruptedException e) { e.printStackTrace(); } }}class MyThread extends Thread{ private volatile boolean flag = true; public void stopTask() { flag = false; } public MyThread(String name) { super(name); } @Override public void run() { synchronized (this) { int i = 0; while(flag) {try { Thread.sleep(100); // 休眠100ms} catch (InterruptedException ie) { System.out.println(Thread.currentThread().getName() +' [ '+this.getState()+' ] catch InterruptedException.'); break;}i++;System.out.println(Thread.currentThread().getName()+' [ '+this.getState()+' ] loop ' + i); } } }}
運(yùn)行結(jié)果
t1 [ NEW ] is new.t1 [ RUNNABLE ] is started.t1 [ RUNNABLE ] loop 1t1 [ RUNNABLE ] loop 2t1 [ RUNNABLE ] loop 3t1 [ RUNNABLE ] is interrupted.t1 [ TERMINATED ] is interrupted now.
四、interrupted() 和 isInterrupted()的區(qū)別
interrupted() 和 isInterrupted()都能夠用于檢測(cè)對(duì)象的“中斷標(biāo)記”。區(qū)別是,interrupted() 除了返回中斷標(biāo)記之外,它還會(huì)清除中斷標(biāo)記(即將中斷標(biāo)記設(shè)為 false);而 isInterrupted() 僅僅返回中斷標(biāo)記。
以上就是深入分析JAVA 多線程--interrupt()和線程終止方式的詳細(xì)內(nèi)容,更多關(guān)于JAVA 多線程--interrupt()和線程終止的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. Python實(shí)現(xiàn)學(xué)生管理系統(tǒng)的代碼(JSON模塊)2. Java類加載機(jī)制實(shí)現(xiàn)步驟解析3. JAMon(Java Application Monitor)備忘記4. Python OpenCV去除字母后面的雜線操作5. Spring security 自定義過(guò)濾器實(shí)現(xiàn)Json參數(shù)傳遞并兼容表單參數(shù)(實(shí)例代碼)6. IntelliJ IDEA設(shè)置背景圖片的方法步驟7. Python os庫(kù)常用操作代碼匯總8. Python TestSuite生成測(cè)試報(bào)告過(guò)程解析9. IntelliJ IDEA設(shè)置默認(rèn)瀏覽器的方法10. 增大python字體的方法步驟
