您的位置:首頁技術文章
文章詳情頁

深入分析JAVA 多線程--interrupt()和線程終止方式

瀏覽:6日期:2022-08-30 15:14:10

一、interrupt() 介紹

interrupt() 定義在 Thread 類中,作用是中斷本線程。

本線程中斷自己是被允許的;其它線程調用本線程的 interrupt() 方法時,會通過 checkAccess() 檢查權限。這有可能拋出 SecurityException 異常。如果本線程是處于阻塞狀態:調用線程的 wait() , wait(long) 或 wait(long, int) 會讓它進入等待(阻塞)狀態,或者調用線程的 join(),join(long),join(long, int),sleep(long),sleep(long, int) 也會讓它進入阻塞狀態。若線程在阻塞狀態時,調用了它的 interrupt() 方法,那么它的“中斷狀態”會被清除并且會收到一個 InterruptedException 異常。例如,線程通過 wait() 進入阻塞狀態,此時通過 interrupt() 中斷該線程;調用 interrupt() 會立即將線程的中斷標記設為 true,但是由于線程處于阻塞狀態,所以該“中斷標記”會立即被清除為 “false”,同時,會產生一個 InterruptedException 的異常。如果線程被阻塞在一個 Selector 選擇器中,那么通過 interrupt() 中斷它時;線程的中斷標記會被設置為 true,并且它會立即從選擇操作中返回。如果不屬于前面所說的情況,那么通過 interrupt() 中斷線程時,它的中斷標記會被設置為 true。中斷一個“已終止的線程”不會產生任何操作。

二、線程終止方式

Thread中的 stop() 和 suspend() 方法,由于固有的不安全性,已經建議不再使用!下面,我先分別討論線程在“阻塞狀態”和“運行狀態”的終止方式,然后再總結出一個通用的方式。

(一)、終止處于“阻塞狀態”的線程.

通常,我們通過“中斷”方式終止處于“阻塞狀態”的線程。當線程由于被調用了 sleep(),,wait(),join() 等方法而進入阻塞狀態;若此時調用線程的 interrupt() 將線程的中斷標記設為 true。由于處于阻塞狀態,中斷標記會被清除,同時產生一個InterruptedException 異常。將 InterruptedException 放在適當的位置就能終止線程,形式如下:

public void run() { try { while (true) { // 執行業務 } } catch (InterruptedException ie) { // 由于產生InterruptedException異常,退出while(true)循環,線程終止! }}

說明:

在while(true)中不斷的執行業務代碼,當線程處于阻塞狀態時,調用線程的 interrupt() 產生 InterruptedException 中斷。中斷的捕獲在 while(true) 之外,這樣就退出了 while(true) 循環!

注意:

對 InterruptedException 的捕獲務一般放在 while(true) 循環體的外面,這樣,在產生異常時就退出了 while(true) 循環。否則,InterruptedException 在 while(true) 循環體之內,就需要額外的添加退出處理。形式如下:

public void run() { while (true) { try { // 執行任務... } catch (InterruptedException ie) { // InterruptedException在while(true)循環體內。 // 當線程產生了InterruptedException異常時,while(true)仍能繼續運行!需要手動退出 break; } }}

說明:

上面的 InterruptedException 異常的捕獲在 whle(true) 之內。當產生 InterruptedException 異常時,被 catch 處理之外,仍然在 while(true) 循環體內;要退出 while(true) 循環體,需要額外的執行退出while(true) 的操作。

(二)、終止處于“運行狀態”的線程

通常,我們通過“標記”方式終止處于“運行狀態”的線程。其中,包括“中斷標記”和“額外添加標記”。

1、通過“中斷標記”終止線程

public void run() { while (!isInterrupted()) { // 執行任務... }}

說明:

isInterrupted() 是判斷線程的中斷標記是不是為 true。當線程處于運行狀態,并且我們需要終止它時;可以調用線程的 interrupt() 方法,使用線程的中斷標記為 true,即 isInterrupted() 會返回true。此時,就會退出while循環。

注意:interrupt() 并不會終止處于“運行狀態”的線程!它會將線程的中斷標記設為 true。

2、通過“額外添加標記”終止線程

private volatile boolean flag= true;protected void stopTask() { flag = false;}public void run() { while (flag) { // 執行任務... }}

說明:

線程中有一個 flag 標記,它的默認值是 true;并且我們提供 stopTask() 來設置 flag 標記。當我們需要終止該線程時,調用該線程的 stopTask() 方法就可以讓線程退出 while 循環。

注意:將 flag 定義為 volatile 類型,是為了保證 flag 的可見性。即其它線程通過 stopTask() 修改了 flag 之后,本線程能看到修改后的 flag 的值。

(三)、通過方式

綜合線程處于“阻塞狀態”和“運行狀態”的終止方式,比較通用的終止線程的形式如下:

public void run() { try { // 1. isInterrupted()保證,只要中斷標記為true就終止線程。 while (!isInterrupted()) { // 執行任務... } } catch (InterruptedException ie) { // 2. InterruptedException異常保證,當InterruptedException異常產生時,線程被終止。 }}

1、isInterrupted()保證,只要中斷標記為 true 就終止線程。2、InterruptedException 異常保證,當 InterruptedException 異常產生時,線程被終止。

三、示例

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();// 啟動線程t1 System.out.println(t1.getName() + '[' + t1.getState() + '] is started.'); Thread.sleep(300);// 休眠300毫秒,然后主線程給t1發“中斷”指令,查看t1狀態 t1.interrupt(); System.out.println(t1.getName() + '[' + t1.getState() + '] is interrupted.'); Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態 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'); } }}

運行結果

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.

說明:

①、主線程 main 中通過 new MyThread('t1') 創建線程 t1,之后通過 t1.start() 啟動線程 t1。

②、t1 啟動之后,會不斷的檢查它的中斷標記,如果中斷標記為“false”;則休眠 100ms。

③、t1 休眠之后,會切換到主線程main;主線程再次運行時,會執行t1.interrupt()中斷線程t1。t1收到中斷指令之后,會將t1的中斷標記設置“false”,而且會拋出 InterruptedException 異常。在 t1 的 run() 方法中,是在循環體 while 之外捕獲的異常;因此循環被終止。

我們對上面的結果進行小小的修改,將run()方法中捕獲InterruptedException異常的代碼塊移到while循環體內。

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();// 啟動線程t1 System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is started.'); Thread.sleep(300);// 休眠300毫秒,然后主線程給t1發“中斷”指令,查看t1狀態 t1.interrupt(); System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is interrupted.'); Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態 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); } }}

運行結果

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...... // 無限循環

說明:

程序進入了死循環了。

這是因為,t1在“等待(阻塞)狀態”時,被 interrupt() 中斷;此時,會清除中斷標記(即 isInterrupted() 會返回 false),而且會拋出 InterruptedException 異常(該異常在while循環體內被捕獲)。因此,t1理所當然的會進入死循環了。

解決該問題,需要我們在捕獲異常時,額外的進行退出 while 循環的處理。例如,在 MyThread 的 catch(InterruptedException) 中添加 break 或 return 就能解決該問題。

下面是通過“額外添加標記”的方式終止“狀態狀態”的線程的示例:

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();// 啟動線程t1 System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is started.'); Thread.sleep(300);// 休眠300毫秒,然后主線程給t1發“中斷”指令,查看t1狀態 t1.stopTask(); System.out.println(t1.getName() + ' [ ' + t1.getState() + ' ] is interrupted.'); Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態 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); } } }}

運行結果

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()的區別

interrupted() 和 isInterrupted()都能夠用于檢測對象的“中斷標記”。區別是,interrupted() 除了返回中斷標記之外,它還會清除中斷標記(即將中斷標記設為 false);而 isInterrupted() 僅僅返回中斷標記。

以上就是深入分析JAVA 多線程--interrupt()和線程終止方式的詳細內容,更多關于JAVA 多線程--interrupt()和線程終止的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
国产综合久久一区二区三区