本章,會對線程的interrupt()中斷和終止方式進行介紹。涉及到的內容包括:
1. interrupt()說明
2. 終止線程的方式
2.1 終止處於「阻塞狀態」的線程
2.2 終止處於「運行狀態」的線程
3. 終止線程的示例
4. interrupted() 和 isInterrupted()的區別html
轉載請註明出處:http://www.cnblogs.com/skywang12345/p/3479949.htmljava
在介紹終止線程的方式以前,有必要先對interrupt()進行了解。
關於interrupt(),java的djk文檔描述以下:http://docs.oracle.com/javase/7/docs/api/api
Interrupts this thread.
Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.
If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.
If none of the previous conditions hold then this thread's interrupt status will be set.
Interrupting a thread that is not alive need not have any effect.
大體意思是:安全
interrupt()的做用是中斷本線程。 本線程中斷本身是被容許的;其它線程調用本線程的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放在適當的爲止就能終止線程,形式以下:oracle
@Override 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)循環體以內,就須要額外的添加退出處理。形式以下:less
@Override 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)的操做。ide
一般,咱們經過「標記」方式終止處於「運行狀態」的線程。其中,包括「中斷標記」和「額外添加標記」。
(01) 經過「中斷標記」終止線程。
形式以下:oop
@Override public void run() { while (!isInterrupted()) { // 執行任務... } }
說明:isInterrupted()是判斷線程的中斷標記是否是爲true。當線程處於運行狀態,而且咱們須要終止它時;能夠調用線程的interrupt()方法,使用線程的中斷標記爲true,即isInterrupted()會返回true。此時,就會退出while循環。
注意:interrupt()並不會終止處於「運行狀態」的線程!它會將線程的中斷標記設爲true。this
(02) 經過「額外添加標記」。
形式以下:
private volatile boolean flag= true; protected void stopTask() { flag = false; } @Override public void run() { while (flag) { // 執行任務... } }
說明:線程中有一個flag標記,它的默認值是true;而且咱們提供stopTask()來設置flag標記。當咱們須要終止該線程時,調用該線程的stopTask()方法就可讓線程退出while循環。
注意:將flag定義爲volatile類型,是爲了保證flag的可見性。即其它線程經過stopTask()修改了flag以後,本線程能看到修改後的flag的值。
綜合線程處於「阻塞狀態」和「運行狀態」的終止方式,比較通用的終止線程的形式以下:
@Override public void run() { try { // 1. isInterrupted()保證,只要中斷標記爲true就終止線程。 while (!isInterrupted()) { // 執行任務... } } catch (InterruptedException ie) { // 2. InterruptedException異常保證,當InterruptedException異常產生時,線程被終止。 } }
interrupt()經常被用來終止「阻塞狀態」線程。參考下面示例:
1 // Demo1.java的源碼 2 class MyThread extends Thread { 3 4 public MyThread(String name) { 5 super(name); 6 } 7 8 @Override 9 public void run() { 10 try { 11 int i=0; 12 while (!isInterrupted()) { 13 Thread.sleep(100); // 休眠100ms 14 i++; 15 System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); 16 } 17 } catch (InterruptedException e) { 18 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); 19 } 20 } 21 } 22 23 public class Demo1 { 24 25 public static void main(String[] args) { 26 try { 27 Thread t1 = new MyThread("t1"); // 新建「線程t1」 28 System.out.println(t1.getName() +" ("+t1.getState()+") is new."); 29 30 t1.start(); // 啓動「線程t1」 31 System.out.println(t1.getName() +" ("+t1.getState()+") is started."); 32 33 // 主線程休眠300ms,而後主線程給t1發「中斷」指令。 34 Thread.sleep(300); 35 t1.interrupt(); 36 System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted."); 37 38 // 主線程休眠300ms,而後查看t1的狀態。 39 Thread.sleep(300); 40 System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now."); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 } 45 }
運行結果:
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) catch InterruptedException. t1 (TERMINATED) is interrupted now.
結果說明:
(01) 主線程main中經過new MyThread("t1")建立線程t1,以後經過t1.start()啓動線程t1。
(02) t1啓動以後,會不斷的檢查它的中斷標記,若是中斷標記爲「false」;則休眠100ms。
(03) t1休眠以後,會切換到主線程main;主線程再次運行時,會執行t1.interrupt()中斷線程t1。t1收到中斷指令以後,會將t1的中斷標記設置「false」,並且會拋出InterruptedException異常。在t1的run()方法中,是在循環體while以外捕獲的異常;所以循環被終止。
咱們對上面的結果進行小小的修改,將run()方法中捕獲InterruptedException異常的代碼塊移到while循環體內。
1 // Demo2.java的源碼 2 class MyThread extends Thread { 3 4 public MyThread(String name) { 5 super(name); 6 } 7 8 @Override 9 public void run() { 10 int i=0; 11 while (!isInterrupted()) { 12 try { 13 Thread.sleep(100); // 休眠100ms 14 } catch (InterruptedException ie) { 15 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); 16 } 17 i++; 18 System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); 19 } 20 } 21 } 22 23 public class Demo2 { 24 25 public static void main(String[] args) { 26 try { 27 Thread t1 = new MyThread("t1"); // 新建「線程t1」 28 System.out.println(t1.getName() +" ("+t1.getState()+") is new."); 29 30 t1.start(); // 啓動「線程t1」 31 System.out.println(t1.getName() +" ("+t1.getState()+") is started."); 32 33 // 主線程休眠300ms,而後主線程給t1發「中斷」指令。 34 Thread.sleep(300); 35 t1.interrupt(); 36 System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted."); 37 38 // 主線程休眠300ms,而後查看t1的狀態。 39 Thread.sleep(300); 40 System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now."); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 } 45 }
運行結果:
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) catch InterruptedException. t1 (RUNNABLE) loop 3 t1 (RUNNABLE) loop 4 t1 (RUNNABLE) loop 5 t1 (TIMED_WAITING) is interrupted now. t1 (RUNNABLE) loop 6 t1 (RUNNABLE) loop 7 t1 (RUNNABLE) loop 8 t1 (RUNNABLE) loop 9 ...
結果說明:
程序進入了死循環!
爲何會這樣呢?這是由於,t1在「等待(阻塞)狀態」時,被interrupt()中斷;此時,會清除中斷標記[即isInterrupted()會返回false],並且會拋出InterruptedException異常[該異常在while循環體內被捕獲]。所以,t1理所固然的會進入死循環了。
解決該問題,須要咱們在捕獲異常時,額外的進行退出while循環的處理。例如,在MyThread的catch(InterruptedException)中添加break 或 return就能解決該問題。
下面是經過「額外添加標記」的方式終止「狀態狀態」的線程的示例:
1 // Demo3.java的源碼 2 class MyThread extends Thread { 3 4 private volatile boolean flag= true; 5 public void stopTask() { 6 flag = false; 7 } 8 9 public MyThread(String name) { 10 super(name); 11 } 12 13 @Override 14 public void run() { 15 synchronized(this) { 16 try { 17 int i=0; 18 while (flag) { 19 Thread.sleep(100); // 休眠100ms 20 i++; 21 System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); 22 } 23 } catch (InterruptedException ie) { 24 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); 25 } 26 } 27 } 28 } 29 30 public class Demo3 { 31 32 public static void main(String[] args) { 33 try { 34 MyThread t1 = new MyThread("t1"); // 新建「線程t1」 35 System.out.println(t1.getName() +" ("+t1.getState()+") is new."); 36 37 t1.start(); // 啓動「線程t1」 38 System.out.println(t1.getName() +" ("+t1.getState()+") is started."); 39 40 // 主線程休眠300ms,而後主線程給t1發「中斷」指令。 41 Thread.sleep(300); 42 t1.stopTask(); 43 System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted."); 44 45 // 主線程休眠300ms,而後查看t1的狀態。 46 Thread.sleep(300); 47 System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now."); 48 } catch (InterruptedException e) { 49 e.printStackTrace(); 50 } 51 } 52 }
運行結果:
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) loop 3 t1 (TERMINATED) is interrupted now.
最後談談 interrupted() 和 isInterrupted()。
interrupted() 和 isInterrupted()都可以用於檢測對象的「中斷標記」。
區別是,interrupted()除了返回中斷標記以外,它還會清除中斷標記(即將中斷標記設爲false);而isInterrupted()僅僅返回中斷標記。
更多內容
3. Java多線程系列--「基礎篇」02之 經常使用的實現多線程的兩種方式
4. Java多線程系列--「基礎篇」03之 Thread中start()和run()的區別
5. Java多線程系列--「基礎篇」04之 synchronized關鍵字