先看收集了別人的文章,全面的瞭解下java的中斷:html
線程的thread.interrupt()方法是中斷線程,將會設置該線程的中斷狀態位,即設置爲true,中斷的結果線程是死亡、仍是等待新的任務或是繼續運行至下一步,就取決於這個程序自己。線程會不時地檢測這個中斷標示位,以判斷線程是否應該被中斷(中斷標示值是否爲true)。它並不像stop方法那樣會中斷一個正在運行的線程。java
判斷某個線程是否已被髮送過中斷請求,請使用Thread.currentThread().isInterrupted()方法(由於它將線程中斷標示位設置爲true後,不會馬上清除中斷標示位,即不會將中斷標設置爲false),而不要使用thread.interrupted()(該方法調用後會將中斷標示位清除,即從新設置爲false)方法來判斷,下面是線程在循環中時的中斷方式:程序員
while(!Thread.currentThread().isInterrupted() && more work to do){ do more work }
若是一個線程處於了阻塞狀態(如線程調用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中斷的通道上的 I/O 操做方法後可進入阻塞狀態),則在線程在檢查中斷標示時若是發現中斷標示爲true,則會在這些阻塞方法(sleep、join、wait、1.5中的condition.await及可中斷的通道上的 I/O 操做方法)調用處拋出InterruptedException異常,而且在拋出異常後當即將線程的中斷標示位清除,即從新設置爲false。拋出異常是爲了線程從阻塞狀態醒過來,並在結束線程前讓程序員有足夠的時間來處理中斷請求。安全
注,synchronized在獲鎖的過程當中是不能被中斷的,意思是說若是產生了死鎖,則不可能被中斷(請參考後面的測試例子)。與synchronized功能類似的reentrantLock.lock()方法也是同樣,它也不可中斷的,即若是發生死鎖,那麼reentrantLock.lock()方法沒法終止,若是調用時被阻塞,則它一直阻塞到它獲取到鎖爲止。可是若是調用帶超時的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit),那麼若是線程在等待時被中斷,將拋出一個InterruptedException異常,這是一個很是有用的特性,由於它容許程序打破死鎖。你也能夠調用reentrantLock.lockInterruptibly()方法,它就至關於一個超時設爲無限的tryLock方法。服務器
沒有任何語言方面的需求一個被中斷的線程應該終止。中斷一個線程只是爲了引發該線程的注意,被中斷線程能夠決定如何應對中斷。某些線程很是重要,以致於它們應該不理會中斷,而是在處理完拋出的異常以後繼續執行,可是更廣泛的狀況是,一個線程將把中斷看做一個終止請求,這種線程的run方法遵循以下形式:網絡
public void run() { try { ... /* * 無論循環裏是否調用過線程阻塞的方法如sleep、join、wait,這裏仍是須要加上 * !Thread.currentThread().isInterrupted()條件,雖然拋出異常後退出了循環,顯 * 得用阻塞的狀況下是多餘的,但若是調用了阻塞方法但沒有阻塞時,這樣會更安全、更及時。 */ while (!Thread.currentThread().isInterrupted()&& more work to do) { do more work } } catch (InterruptedException e) { //線程在wait或sleep期間被中斷了 } finally { //線程結束前作一些清理工做 } }
上面是while循環在try塊裏,若是try在while循環裏時,因該在catch塊裏從新設置一下中斷標示,由於拋出InterruptedException異常後,中斷標示位會自動清除,此時應該這樣:app
public void run() { while (!Thread.currentThread().isInterrupted()&& more work to do) { try { ... sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt();//從新設置中斷標示 } } }
另外不要在你的底層代碼裏捕獲InterruptedException異常後不處理,會處理不當,以下:jvm
void mySubTask(){ ... try{ sleep(delay); }catch(InterruptedException e){}//不要這樣作 ... }
若是你不知道拋InterruptedException異常後如何處理,那麼你有以下好的建議處理方式:
一、在catch子句中,調用Thread.currentThread.interrupt()來設置中斷狀態(由於拋出異常後中斷標示會被清除),讓外界經過判斷Thread.currentThread().isInterrupted()標示來決定是否終止線程仍是繼續下去,應該這樣作:socket
void mySubTask() { ... try { sleep(delay); } catch (InterruptedException e) { Thread.currentThread().isInterrupted(); } ... }
二、或者,更好的作法就是,不使用try來捕獲這樣的異常,讓方法直接拋出:學習
void mySubTask() throws InterruptedException { ... sleep(delay); ... }
中斷線程最好的,最受推薦的方式是,使用共享變量(shared variable)發出信號,告訴線程必須中止正在運行的任務。線程必須週期性的核查這一變量,而後有秩序地停止任務。Example2描述了這一方式:
class Example2 extends Thread { volatile boolean stop = false;// 線程中斷信號量 public static void main(String args[]) throws Exception { Example2 thread = new Example2(); System.out.println("Starting thread..."); thread.start(); Thread.sleep(3000); System.out.println("Asking thread to stop..."); // 設置中斷信號量 thread.stop = true; Thread.sleep(3000); System.out.println("Stopping application..."); } public void run() { // 每隔一秒檢測一下中斷信號量 while (!stop) { System.out.println("Thread is running..."); long time = System.currentTimeMillis(); /* * 使用while循環模擬 sleep 方法,這裏不要使用sleep,不然在阻塞時會 拋 * InterruptedException異常而退出循環,這樣while檢測stop條件就不會執行, * 失去了意義。 */ while ((System.currentTimeMillis() - time < 1000)) {} } System.out.println("Thread exiting under request..."); } }
雖然Example2該方法要求一些編碼,但並不難實現。同時,它給予線程機會進行必要的清理工做。這裏需注意一點的是需將共享變量定義成volatile 類型或將對它的一切訪問封入同步的塊/方法(synchronized blocks/methods)中。上面是中斷一個非阻塞狀態的線程的常見作法,但對非檢測isInterrupted()條件會更簡潔:
class Example2 extends Thread { public static void main(String args[]) throws Exception { Example2 thread = new Example2(); System.out.println("Starting thread..."); thread.start(); Thread.sleep(3000); System.out.println("Asking thread to stop..."); // 發出中斷請求 thread.interrupt(); Thread.sleep(3000); System.out.println("Stopping application..."); } public void run() { // 每隔一秒檢測是否設置了中斷標示 while (!Thread.currentThread().isInterrupted()) { System.out.println("Thread is running..."); long time = System.currentTimeMillis(); // 使用while循環模擬 sleep while ((System.currentTimeMillis() - time < 1000) ) { } } System.out.println("Thread exiting under request..."); } }
到目前爲止一切順利!可是,當線程等待某些事件發生而被阻塞,又會發生什麼?固然,若是線程被阻塞,它便不能覈查共享變量,也就不能中止。這在許多狀況下會發生,例如調用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()時,這裏僅舉出一些。
他們均可能永久的阻塞線程。即便發生超時,在超時期滿以前持續等待也是不可行和不適當的,因此,要使用某種機制使得線程更早地退出被阻塞的狀態。下面就來看一下中斷阻塞線程技術。
Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,設置線程的中斷標示位,在線程受到阻塞的地方(如調用sleep、wait、join等地方)拋出一個異常InterruptedException,而且中斷狀態也將被清除,這樣線程就得以退出阻塞的狀態。下面是具體實現:
class Example3 extends Thread { public static void main(String args[]) throws Exception { Example3 thread = new Example3(); System.out.println("Starting thread..."); thread.start(); Thread.sleep(3000); System.out.println("Asking thread to stop..."); thread.interrupt();// 等中斷信號量設置後再調用 Thread.sleep(3000); System.out.println("Stopping application..."); } public void run() { while (!Thread.currentThread().isInterrupted()) { System.out.println("Thread running..."); try { /* * 若是線程阻塞,將不會去檢查中斷信號量stop變量,所 以thread.interrupt() * 會使阻塞線程從阻塞的地方拋出異常,讓阻塞線程從阻塞狀態逃離出來,並 * 進行異常塊進行 相應的處理 */ Thread.sleep(1000);// 線程阻塞,若是線程收到中斷操做信號將拋出異常 } catch (InterruptedException e) { System.out.println("Thread interrupted..."); /* * 若是線程在調用 Object.wait()方法,或者該類的 join() 、sleep()方法 * 過程當中受阻,則其中斷狀態將被清除 */ System.out.println(this.isInterrupted());// false //中不中斷由本身決定,若是須要真真中斷線程,則須要從新設置中斷位,若是 //不須要,則不用調用 Thread.currentThread().interrupt(); } } System.out.println("Thread exiting under request..."); } }
一旦Example3中的Thread.interrupt()被調用,線程便收到一個異常,因而逃離了阻塞狀態並肯定應該中止。上面咱們還可使用共享信號量來替換!Thread.currentThread().isInterrupted()條件,但不如它簡潔。
Example4試着去中斷處於死鎖狀態的兩個線程,但這兩個線都沒有收到任何中斷信號(拋出異常),因此interrupt()方法是不能中斷死鎖線程的,由於鎖定的位置根本沒法拋出異常:
class Example4 extends Thread { public static void main(String args[]) throws Exception { final Object lock1 = new Object(); final Object lock2 = new Object(); Thread thread1 = new Thread() { public void run() { deathLock(lock1, lock2); } }; Thread thread2 = new Thread() { public void run() { // 注意,這裏在交換了一下位置 deathLock(lock2, lock1); } }; System.out.println("Starting thread..."); thread1.start(); thread2.start(); Thread.sleep(3000); System.out.println("Interrupting thread..."); thread1.interrupt(); thread2.interrupt(); Thread.sleep(3000); System.out.println("Stopping application..."); } static void deathLock(Object lock1, Object lock2) { try { synchronized (lock1) { Thread.sleep(10);// 不會在這裏死掉 synchronized (lock2) {// 會鎖在這裏,雖然阻塞了,但不會拋異常 System.out.println(Thread.currentThread()); } } } catch (InterruptedException e) { e.printStackTrace(); System.exit(1); } } }
然而,若是線程在I/O操做進行時被阻塞,又會如何?I/O操做能夠阻塞線程一段至關長的時間,特別是牽扯到網絡應用時。例如,服務器可能須要等待一個請求(request),又或者,一個網絡應用程序可能要等待遠端主機的響應。
實現此InterruptibleChannel接口的通道是可中斷的:若是某個線程在可中斷通道上因調用某個阻塞的 I/O 操做(常見的操做通常有這些:serverSocketChannel. accept()、socketChannel.connect、socketChannel.open、socketChannel.read、socketChannel.write、fileChannel.read、fileChannel.write)而進入阻塞狀態,而另外一個線程又調用了該阻塞線程的 interrupt 方法,這將致使該通道被關閉,而且已阻塞線程接將會收到ClosedByInterruptException,而且設置已阻塞線程的中斷狀態。另外,若是已設置某個線程的中斷狀態而且它在通道上調用某個阻塞的 I/O 操做,則該通道將關閉而且該線程當即接收到 ClosedByInterruptException;並仍然設置其中斷狀態。若是狀況是這樣,其代碼的邏輯和第三個例子中的是同樣的,只是異常不一樣而已。
若是你正使用通道(channels)(這是在Java 1.4中引入的新的I/O API),那麼被阻塞的線程將收到一個ClosedByInterruptException異常。可是,你可能正使用Java1.0以前就存在的傳統的I/O,並且要求更多的工做。既然這樣,Thread.interrupt()將不起做用,由於線程將不會退出被阻塞狀態。Example5描述了這一行爲。儘管interrupt()被調用,線程也不會退出被阻塞狀態,好比ServerSocket的accept方法根本不拋出異常。
很幸運,Java平臺爲這種情形提供了一項解決方案,即調用阻塞該線程的套接字的close()方法。在這種情形下,若是線程被I/O操做阻塞,當調用該套接字的close方法時,該線程在調用accept地方法將接收到一個SocketException(SocketException爲IOException的子異常)異常,這與使用interrupt()方法引發一個InterruptedException異常被拋出很是類似,(注,若是是流因讀寫阻塞後,調用流的close方法也會被阻塞,根本不能調用,更不會拋IOExcepiton,此種狀況下怎樣中斷?我想能夠轉換爲通道來操做流能夠解決,好比文件通道)。下面是具體實現:
class Example6 extends Thread { volatile ServerSocket socket; public static void main(String args[]) throws Exception { Example6 thread = new Example6(); System.out.println("Starting thread..."); thread.start(); Thread.sleep(3000); System.out.println("Asking thread to stop..."); Thread.currentThread().interrupt();// 再調用interrupt方法 thread.socket.close();// 再調用close方法 try { Thread.sleep(3000); } catch (InterruptedException e) { } System.out.println("Stopping application..."); } public void run() { try { socket = new ServerSocket(8888); } catch (IOException e) { System.out.println("Could not create the socket..."); return; } while (!Thread.currentThread().isInterrupted()) { System.out.println("Waiting for connection..."); try { socket.accept(); } catch (IOException e) { System.out.println("accept() failed or interrupted..."); Thread.currentThread().interrupt();//從新設置中斷標示位 } } System.out.println("Thread exiting under request..."); } }
---------------------------------------------------------------------------------------------------------------------------------------------------------
1、沒有任何語言方面的需求一個被中斷的線程應該終止。中斷一個線程只是爲了引發該線程的注意,被中斷線程能夠決定如何應對中斷。
2、對於處於sleep,join等操做的線程,若是被調用interrupt()後,會拋出InterruptedException,而後線程的中斷標誌位會由true重置爲false,由於線程爲了處理異常已經從新處於就緒狀態。
3、不可中斷的操做,包括進入synchronized段以及Lock.lock(),inputSteam.read()等,調用interrupt()對於這幾個問題無效,由於它們都不拋出中斷異常。若是拿不到資源,它們會無限期阻塞下去。
對於Lock.lock(),能夠改用Lock.lockInterruptibly(),可被中斷的加鎖操做,它能夠拋出中斷異常。等同於等待時間無限長的Lock.tryLock(long time, TimeUnit unit)。
對於inputStream等資源,有些(實現了interruptibleChannel接口)能夠經過close()方法將資源關閉,對應的阻塞也會被放開。
首先,看看Thread類裏的幾個方法:
public static boolean interrupted | 測試當前線程是否已經中斷。線程的中斷狀態 由該方法清除。換句話說,若是連續兩次調用該方法,則第二次調用將返回 false。 |
public boolean isInterrupted() |
測試線程是否已經中斷。線程的中斷狀態 不受該方法的影響。 |
public void interrupt() |
中斷線程。 |
上面列出了與中斷有關的幾個方法及其行爲,能夠看到interrupt是中斷線程。若是不瞭解Java的中斷機制,這樣的一種解釋極容易形成誤解,認爲調用了線程的interrupt方法就必定會中斷線程。
其實,Java的中斷是一種協做機制。也就是說調用線程對象的interrupt方法並不必定就中斷了正在運行的線程,它只是要求線程本身在合適的時機中斷本身。每一個線程都有一個boolean的中斷狀態(這個狀態不在Thread的屬性上),interrupt方法僅僅只是將該狀態置爲true。
好比對正常運行的線程調用interrupt()並不能終止他,只是改變了interrupt標示符。
通常說來,若是一個方法聲明拋出InterruptedException,表示該方法是可中斷的,好比wait,sleep,join,也就是說可中斷方法會對interrupt調用作出響應(例如sleep響應interrupt的操做包括清除中斷狀態,拋出InterruptedException),異常都是由可中斷方法本身拋出來的,並非直接由interrupt方法直接引發的。
Object.wait, Thread.sleep方法,會不斷的輪詢監聽 interrupted 標誌位,發現其設置爲true後,會中止阻塞並拋出 InterruptedException異常。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
看了以上的說明,對java中斷的使用確定是會了,但我想知道的是阻塞了的線程是如何經過interuppt方法完成中止阻塞並拋出interruptedException的,這就要看Thread中native的interuppt0方法了。
第一步學習Java的JNI調用Native方法。
第二步下載openjdk的源代碼,找到目錄結構裏的openjdk-src\jdk\src\share\native\java\lang\Thread.c文件。
#include "jni.h" #include "jvm.h" #include "java_lang_Thread.h" #define THD "Ljava/lang/Thread;" #define OBJ "Ljava/lang/Object;" #define STE "Ljava/lang/StackTraceElement;" #define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"yield", "()V", (void *)&JVM_Yield}, {"sleep", "(J)V", (void *)&JVM_Sleep}, {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, }; #undef THD #undef OBJ #undef STE JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); }
暫時還看不太懂,先去學習一下C的一些基礎。
未完待續...