如果你使用過殺毒軟件,可能會發現全盤殺毒太耗時間了,這時你如果點擊取消殺毒按鈕,那么此時你正在中斷一個運行的線程。
java為我們提供了一種調用interrupt()方法來請求終止線程的方法,下面我們就一起來學習一下線程的中斷。
每一個線程都有一個boolean類型標志,用來表明當前線程是否請求中斷,當一個線程調用interrupt() 方法時,線程的中斷標志將被設置為true。
我們可以通過調用Thread.currentThread().isInterrupted()或者Thread.interrupted()來檢測線程的中斷標志是否被置位。這兩個方法的區別是
Thread.currentThread().isInterrupted()是線程對象的方法,調用它后不清除線程中斷標志位;而Thread.interrupted()是一個靜態方法,調用它會清除
線程中斷標志位。
Thread.currentThread().isInterrupted(): 對象方法 不清除中斷標志位
Thread.interrupted(): 靜態方法 清除中斷標志位(設置為false)
所以說調用線程的interrupt() 方法不會中斷一個正在運行的線程,這個機制只是設置了一個線程中斷標志位,如果在程序中你不檢測線程中斷標志位,那么即使
設置了中斷標志位為true,線程也一樣照常運行。
一般來說中斷線程分為三種情況:
(一) :中斷非阻塞線程
(二):中斷阻塞線程
(三):不可中斷線程
(一) :中斷非阻塞線程
中斷非阻塞線程通常有兩種方式:
(1)采用線程共享變量
這種方式比較簡單可行,需要注意的一點是共享變量必須設置為volatile,這樣才能保證修改后其他線程立即可見。
public class InterruptThreadTest extends Thread{ // 設置線程共享變量 volatile boolean isStop = false; public void run() { while(!isStop) { long beginTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + "is running"); // 當前線程每隔一秒鍾檢測一次線程共享變量是否得到通知 while (System.currentTimeMillis() - beginTime < 1000) {} } if (isStop) { System.out.println(Thread.currentThread().getName() + "is interrupted"); } } public static void main(String[] args) { // TODO Auto-generated method stub InterruptThreadTest itt = new InterruptThreadTest(); itt.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 線程共享變量設置為true itt.isStop = true; } }
(2) 采用中斷機制
代碼如下:
public class InterruptThreadTest2 extends Thread{ public void run() { // 這里調用的是非清除中斷標志位的isInterrupted方法 while(!Thread.currentThread().isInterrupted()) { long beginTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + "is running"); // 當前線程每隔一秒鍾檢測線程中斷標志位是否被置位 while (System.currentTimeMillis() - beginTime < 1000) {} } if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + "is interrupted"); } } public static void main(String[] args) { // TODO Auto-generated method stub InterruptThreadTest2 itt = new InterruptThreadTest2(); itt.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 設置線程的中斷標志位 itt.interrupt(); } }
(二):中斷阻塞線程
當線程調用Thread.sleep()、Thread.join()、object.wait()再或者調用阻塞的i/o操作方法時,都會使得當前線程進入阻塞狀態。那么此時如果在線程處於阻塞狀態是調用
interrupt() 方法設置線程中斷標志位時會出現什么情況呢! 此時處於阻塞狀態的線程會拋出一個異常,並且會清除線程中斷標志位(設置為false)。這樣一來線程就能退出
阻塞狀態。當然拋出異常的方法就是造成線程處於阻塞狀態的Thread.sleep()、Thread.join()、object.wait()這些方法。
代碼實例如下:
public class InterruptThreadTest3 extends Thread{ public void run() { // 這里調用的是非清除中斷標志位的isInterrupted方法 while(!Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + " is running"); try { System.out.println(Thread.currentThread().getName() + " Thread.sleep begin"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " Thread.sleep end"); } catch (InterruptedException e) { // TODO Auto-generated catch block //由於調用sleep()方法清除狀態標志位 所以這里需要再次重置中斷標志位 否則線程會繼續運行下去 Thread.currentThread().interrupt(); e.printStackTrace(); } } if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + "is interrupted"); } } public static void main(String[] args) { // TODO Auto-generated method stub InterruptThreadTest3 itt = new InterruptThreadTest3(); itt.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 設置線程的中斷標志位 itt.interrupt(); } }
需要注意的地方就是 Thread.sleep()、Thread.join()、object.wait()這些方法,會檢測線程中斷標志位,如果發現中斷標志位為true則拋出異常並且將中斷標志位設置為false。
所以while循環之后每次調用阻塞方法后 都要在捕獲異常之后,調用Thread.currentThread().interrupt()重置狀態標志位。
(三):不可中斷線程
有一種情況是線程不能被中斷的,就是調用synchronized關鍵字和reentrantLock.lock()獲取鎖的過程。
但是如果調用帶超時的tryLock方法reentrantLock.tryLock(longtimeout, TimeUnit unit),那么如果線程在等待時被中斷,將拋出一個InterruptedException異常,這是一個非常
有用的特性,因為它允許程序打破死鎖。你也可以調用reentrantLock.lockInterruptibly()方法,它就相當於一個超時設為無限的tryLock方法。
public class InterruptThreadTest5 { public void deathLock(Object lock1, Object lock2) { try { synchronized (lock1) { System.out.println(Thread.currentThread().getName()+ " is running"); // 讓另外一個線程獲得另一個鎖 Thread.sleep(10); // 造成死鎖 synchronized (lock2) { System.out.println(Thread.currentThread().getName()); } } } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+ " is interrupted"); e.printStackTrace(); } } public static void main(String [] args) { final InterruptThreadTest5 itt = new InterruptThreadTest5(); final Object lock1 = new Object(); final Object lock2 = new Object(); Thread t1 = new Thread(new Runnable(){ public void run() { itt.deathLock(lock1, lock2); } },"A"); Thread t2 = new Thread(new Runnable(){ public void run() { itt.deathLock(lock2, lock1); } },"B"); t1.start(); t2.start(); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 中斷線程t1、t2 t1.interrupt(); t2.interrupt(); } }
其它學習文章參考: