中斷Java線程
發表時間:2024-02-09 來源:明輝站整理相關軟件相關文章人氣:
[摘要]由于可能導致異常行為的產生,多線程技術顯然對于開發人員來說提出了一系列新的挑戰。本文,我們將就這些挑戰之一:如何中斷一個正在運行的線程展開討論。 在Java中通過其內建的線程支持,編寫多線程的程序還是相當簡單的。然而,采用多線程技術將對程序開發人員提出了一些列的挑戰,如果沒有得到正確的處理,可能...
由于可能導致異常行為的產生,多線程技術顯然對于開發人員來說提出了一系列新的挑戰。本文,我們將就這些挑戰之一:如何中斷一個正在運行的線程展開討論。
在Java中通過其內建的線程支持,編寫多線程的程序還是相當簡單的。然而,采用多線程技術將對程序開發人員提出了一些列的挑戰,如果沒有得到正確的處理,可能會導致異常行為的產生,以及難以發現的差錯。本文,我們將就這些挑戰之一:如何中斷一個正在運行的線程展開討論。
背景
中斷一個線程意味著在完成其任務以前,停止線程正在進行的工作,即有效的中止當前操作。線程中斷后是等待新的任務還是繼續進行下一步操作將取決于應用程序。
盡管在最初看起來比較簡單,你還是需要預先采取一些措施以求獲得理想的結果。這里就你必須注意的問題提出了一些建議:
首先,不要使用Thread.stop方法。盡管它的確可以中止一個正在運行的線程,但這樣的方法并不安全,并遭到了開發人員普遍的反對。這也可能意味著在未來的Java版本中它可能不會出現。
另一種并不建議的方法是Thread.interrupt。有人可能會將其與上文提到的方法相混淆。不論它的名字表示什么,這種方法事實上并沒有立即中斷一個正在運行的線程(后來也不會),如列表A所示。它創建了一個線程,并且嘗試使用Thread.interrupt來停止此線程。對Thread.sleep()的調用提供了充裕的時間來進行線程的初始化和結束。線程本身并沒有做任何有用的事情。
如果運行列表A中的代碼,在控制臺中你可以看到類似的如下內容:
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Interrupting thread...
Thread is running...
Thread is running...
Thread is running...
Stopping application...
即使在調用Thread.interrupt()之后,線程還是運行了一段時間。
真正的中斷一個線程
中斷一個線程的最好的推薦方法是使用一個共享變量來指示線程必須中止目前所做的工作。線程必須周期性的對變量進行檢查,尤其在處理較長的操作的時候,然后通過有序的方式中止線程任務。列表B的代碼給出了此技術的具體實現:
運行列表B中的代碼將會產生如下輸出(注意線程在前一種方法中是如何退出的):
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Asking thread to stop...
Thread exiting under request...
Stopping application...
盡管這樣的方法需要編寫一定量代碼,但這并不會給執行這些線程以及根據需要對線程進行清除帶來多大麻煩,而尤其是清除線程對于任何一個多線程的應用程序來說都是絕對必需的。只需要確保已經對共享變量聲明為可變,或者將任何對其的訪問封裝在同步代碼塊或方法里面。
到現在為止,一切都很順利。但如果線程被封鎖以等待一些事件,那么將會發生些什么?當然,如果線程被封鎖,它將不能對共享變量進行檢查,從而無法停止。有很多時候會發生這樣的情況,諸如對Object.wait()、ServerSocket.accept()以及DatagramSocket.receive()瞪函數進行調用的時候。
這些函數都能夠將線程永遠的封鎖起來。即使采用了超時機制,或許也不會可行,或者讓人無法忍受線程一直運行直到到達超時狀態。所以必須采用某種機制以使線程提早的退出封鎖狀態。
不幸的是,這里還沒有這樣的機制能夠適用于所有的情況。但可以根據具體的情況來使用一些特定的技巧。在如下的部分,我將給出對于絕大部分的常見情況所采取的解決方案。
通過Thread.interrupt()中斷一個線程
如列表A所示,采用Thread.interrupt()方法并沒有中斷一個正在運行的線程。此方法事實上做的只是如果線程被封鎖則拋出一個中斷信號,由此線程退出了封鎖狀態。更為精確的講,如果線程被封鎖在方法Object.wait、Thread.join或是Thread.sleep,它將接收一個InterruptedException,從而提前終結封鎖方法。
因此,如果一個線程被封鎖在上述方法中的任意一個,停止它的正確方法是設置共享變量,并對其調用interrupt()方法(注意首先設置變量非常重要)。如果線程沒有被鎖定,調用interrupt()則無關緊要,否則,線程將會得到一個異常(線程本身必須準備好處理這種情況)然后退出鎖定狀態。在任何一種情況中,最終線程都將會檢測共享變量并終止。列表C的簡單范例程序表明了這一技術的運用。
一旦Thread.interrupt()在列表C代碼中得到調用,線程將獲得一個異常,于是它退出了封鎖狀態并決定它應當停止。運行這些代碼將輸出一下結果:
Starting thread...
Thread running...
Thread running...
Thread running...
Asking thread to stop...
Thread interrupted...
Thread exiting under request...
Stopping application...
對I/O操作進行中斷
但如果線程是在執行I/O操作時被封鎖將如何解決?I/O可以在一段可觀的時間里保持對一個線程的封鎖,尤其涉及網絡通信的時候。比如,一個服務器可能會等待用戶請求,或者一個網絡應用程序會等待遠程主機的響應。
如果你正在使用通道——在Java 1.4中可以獲得的新的I/O API——封鎖的線程將會得到一個ClosedByInterruptException異常。如果是這種情況,處理的邏輯方法與在第三個例子中所使用的相同——不同的僅僅是產生的異常有所區別。
但是,由于新的I/O最近才發布并且還有待進一步研究,你也可能還在使用Java 1.0以來一直提供的傳統的I/O。這種情況下,使用Thread.interrupt()不會產生任何幫助,原因是線程將不會退出封鎖狀態。列表D中的代碼顯示了這一過程。盡管調用了interrupt()方法,線程還是沒有退出封鎖狀態。
值得慶幸的是,Java Platform提供了在這種情況下的解決方案:通過調用鎖定線程的socket的close()方法。這樣的情況下,如果線程是在進行I/O操作時被鎖定,線程將得到一個SocketException異常,這非常類似于interrupt()方法引發了InterruptedException異常的情況。
唯一需要提醒的地方是對socket的引用必須可用,這樣的話close()方法才能被調用。這也意味著socket對象也必須被共享。列表E顯示了這種情況。處理的邏輯過程與以前給出的范例相同。
運行列表E中代碼將會得到如下預期結果:
Starting thread...
Waiting for connection...
Asking thread to stop...
accept() failed or interrupted...
Thread exiting under request...
Stopping application...
多線程是功能非常強大的工具,但也給自身的處理帶來了一些列挑戰。其中之一便是如何中斷一個正在運行的線程。如果處理得當,使用這些技術中斷線程的難度將不會大于使用Java Platform提供的內建操作完成同樣的任務。
列表A:通過Thread.interrupt()中斷線程
class Example1 extends Thread {
public static void main( String args[] ) throws Exception {
Example1 thread = new Example1();
System.out.println( "Starting thread..." );
thread.start();
Thread.sleep( 3000 );
System.out.println( "Interrupting thread..." );
thread.interrupt();
Thread.sleep( 3000 );
System.out.println( "Stopping application..." );
System.exit( 0 );
}
public void run() {
while ( true ) {
System.out.println( "Thread is running..." );
long time = System.currentTimeMillis();
while ( System.currentTimeMillis()-time < 1000 ) {
}
}
}
}
列表B:通過傳遞信號中斷線程
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..." );
System.exit( 0 );
}
public void run() {
while ( !stop ) {
System.out.println( "Thread is running..." );
long time = System.currentTimeMillis();
while ( (System.currentTimeMillis()-time < 1000) && (!stop) ) {
}
}
System.out.println( "Thread exiting under request..." );
}
}
列表C:通過Thread.interrupt()退出封鎖狀態
class Example3 extends Thread {
volatile boolean stop = false;
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.stop = true;
thread.interrupt();
Thread.sleep( 3000 );
System.out.println( "Stopping application..." );
System.exit( 0 );
}
public void run() {
while ( !stop ) {
System.out.println( "Thread running..." );
try {
Thread.sleep( 1000 );
} catch ( InterruptedException e ) {
System.out.println( "Thread interrupted..." );
}
}
System.out.println( "Thread exiting under request..." );
}
}
列表D:通過Thread.interrupt()中斷I/O操作
import java.io.*;
class Example4 extends Thread {
public static void main( String args[] ) throws Exception {
Example4 thread = new Example4();
System.out.println( "Starting thread..." );
thread.start();
Thread.sleep( 3000 );
System.out.println( "Interrupting thread..." );
thread.interrupt();
Thread.sleep( 3000 );
System.out.println( "Stopping application..." );
System.exit( 0 );
}
public void run() {
ServerSocket socket;
try {
socket = new ServerSocket(7856);
} catch ( IOException e ) {
System.out.println( "Could not create the socket..." );
return;
}
while ( true ) {
System.out.println( "Waiting for connection..." );
try {
Socket sock = socket.accept();
} catch ( IOException e ) {
System.out.println( "accept() failed or interrupted..." );
}
}
}
}
列表E:I/O操作失敗
import java.net.*;
import java.io.*;
class Example5 extends Thread {
volatile boolean stop = false;
volatile ServerSocket socket;
public static void main( String args[] ) throws Exception {
Example5 thread = new Example5();
System.out.println( "Starting thread..." );
thread.start();
Thread.sleep( 3000 );
System.out.println( "Asking thread to stop..." );
thread.stop = true;
thread.socket.close();
Thread.sleep( 3000 );
System.out.println( "Stopping application..." );
System.exit( 0 );
}
public void run() {
try {
socket = new ServerSocket(7856);
} catch ( IOException e ) {
System.out.println( "Could not create the socket..." );
return;
}
while ( !stop ) {
System.out.println( "Waiting for connection..." );
try {
Socket sock = socket.accept();
} catch ( IOException e ) {
System.out.println( "accept() failed or interrupted..." );
}
}
System.out.println( "Thread exiting under request..." );
}
}