明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

中斷Java線程

[摘要]由于可能導(dǎo)致異常行為的產(chǎn)生,多線程技術(shù)顯然對于開發(fā)人員來說提出了一系列新的挑戰(zhàn)。本文,我們將就這些挑戰(zhàn)之一:如何中斷一個(gè)正在運(yùn)行的線程展開討論。 在Java中通過其內(nèi)建的線程支持,編寫多線程的程序還是相當(dāng)簡單的。然而,采用多線程技術(shù)將對程序開發(fā)人員提出了一些列的挑戰(zhàn),如果沒有得到正確的處理,可能...
由于可能導(dǎo)致異常行為的產(chǎn)生,多線程技術(shù)顯然對于開發(fā)人員來說提出了一系列新的挑戰(zhàn)。本文,我們將就這些挑戰(zhàn)之一:如何中斷一個(gè)正在運(yùn)行的線程展開討論。



在Java中通過其內(nèi)建的線程支持,編寫多線程的程序還是相當(dāng)簡單的。然而,采用多線程技術(shù)將對程序開發(fā)人員提出了一些列的挑戰(zhàn),如果沒有得到正確的處理,可能會(huì)導(dǎo)致異常行為的產(chǎn)生,以及難以發(fā)現(xiàn)的差錯(cuò)。本文,我們將就這些挑戰(zhàn)之一:如何中斷一個(gè)正在運(yùn)行的線程展開討論。

背景
中斷一個(gè)線程意味著在完成其任務(wù)以前,停止線程正在進(jìn)行的工作,即有效的中止當(dāng)前操作。線程中斷后是等待新的任務(wù)還是繼續(xù)進(jìn)行下一步操作將取決于應(yīng)用程序。

盡管在最初看起來比較簡單,你還是需要預(yù)先采取一些措施以求獲得理想的結(jié)果。這里就你必須注意的問題提出了一些建議:

首先,不要使用Thread.stop方法。盡管它的確可以中止一個(gè)正在運(yùn)行的線程,但這樣的方法并不安全,并遭到了開發(fā)人員普遍的反對。這也可能意味著在未來的Java版本中它可能不會(huì)出現(xiàn)。

另一種并不建議的方法是Thread.interrupt。有人可能會(huì)將其與上文提到的方法相混淆。不論它的名字表示什么,這種方法事實(shí)上并沒有立即中斷一個(gè)正在運(yùn)行的線程(后來也不會(huì)),如列表A所示。它創(chuàng)建了一個(gè)線程,并且嘗試使用Thread.interrupt來停止此線程。對Thread.sleep()的調(diào)用提供了充裕的時(shí)間來進(jìn)行線程的初始化和結(jié)束。線程本身并沒有做任何有用的事情。

如果運(yùn)行列表A中的代碼,在控制臺(tái)中你可以看到類似的如下內(nèi)容:

Starting thread...

Thread is running...

Thread is running...

Thread is running...

Interrupting thread...

Thread is running...

Thread is running...

Thread is running...

Stopping application...

即使在調(diào)用Thread.interrupt()之后,線程還是運(yùn)行了一段時(shí)間。

真正的中斷一個(gè)線程
中斷一個(gè)線程的最好的推薦方法是使用一個(gè)共享變量來指示線程必須中止目前所做的工作。線程必須周期性的對變量進(jìn)行檢查,尤其在處理較長的操作的時(shí)候,然后通過有序的方式中止線程任務(wù)。列表B的代碼給出了此技術(shù)的具體實(shí)現(xiàn):



運(yùn)行列表B中的代碼將會(huì)產(chǎn)生如下輸出(注意線程在前一種方法中是如何退出的):

Starting thread...

Thread is running...

Thread is running...

Thread is running...

Asking thread to stop...

Thread exiting under request...

Stopping application...



盡管這樣的方法需要編寫一定量代碼,但這并不會(huì)給執(zhí)行這些線程以及根據(jù)需要對線程進(jìn)行清除帶來多大麻煩,而尤其是清除線程對于任何一個(gè)多線程的應(yīng)用程序來說都是絕對必需的。只需要確保已經(jīng)對共享變量聲明為可變,或者將任何對其的訪問封裝在同步代碼塊或方法里面。

到現(xiàn)在為止,一切都很順利。但如果線程被封鎖以等待一些事件,那么將會(huì)發(fā)生些什么?當(dāng)然,如果線程被封鎖,它將不能對共享變量進(jìn)行檢查,從而無法停止。有很多時(shí)候會(huì)發(fā)生這樣的情況,諸如對Object.wait()、ServerSocket.accept()以及DatagramSocket.receive()瞪函數(shù)進(jìn)行調(diào)用的時(shí)候。

這些函數(shù)都能夠?qū)⒕程永遠(yuǎn)的封鎖起來。即使采用了超時(shí)機(jī)制,或許也不會(huì)可行,或者讓人無法忍受線程一直運(yùn)行直到到達(dá)超時(shí)狀態(tài)。所以必須采用某種機(jī)制以使線程提早的退出封鎖狀態(tài)。

不幸的是,這里還沒有這樣的機(jī)制能夠適用于所有的情況。但可以根據(jù)具體的情況來使用一些特定的技巧。在如下的部分,我將給出對于絕大部分的常見情況所采取的解決方案。

通過Thread.interrupt()中斷一個(gè)線程



如列表A所示,采用Thread.interrupt()方法并沒有中斷一個(gè)正在運(yùn)行的線程。此方法事實(shí)上做的只是如果線程被封鎖則拋出一個(gè)中斷信號,由此線程退出了封鎖狀態(tài)。更為精確的講,如果線程被封鎖在方法Object.wait、Thread.join或是Thread.sleep,它將接收一個(gè)InterruptedException,從而提前終結(jié)封鎖方法。

因此,如果一個(gè)線程被封鎖在上述方法中的任意一個(gè),停止它的正確方法是設(shè)置共享變量,并對其調(diào)用interrupt()方法(注意首先設(shè)置變量非常重要)。如果線程沒有被鎖定,調(diào)用interrupt()則無關(guān)緊要,否則,線程將會(huì)得到一個(gè)異常(線程本身必須準(zhǔn)備好處理這種情況)然后退出鎖定狀態(tài)。在任何一種情況中,最終線程都將會(huì)檢測共享變量并終止。列表C的簡單范例程序表明了這一技術(shù)的運(yùn)用。

一旦Thread.interrupt()在列表C代碼中得到調(diào)用,線程將獲得一個(gè)異常,于是它退出了封鎖狀態(tài)并決定它應(yīng)當(dāng)停止。運(yùn)行這些代碼將輸出一下結(jié)果:

Starting thread...

Thread running...

Thread running...

Thread running...

Asking thread to stop...

Thread interrupted...

Thread exiting under request...

Stopping application...



對I/O操作進(jìn)行中斷

但如果線程是在執(zhí)行I/O操作時(shí)被封鎖將如何解決?I/O可以在一段可觀的時(shí)間里保持對一個(gè)線程的封鎖,尤其涉及網(wǎng)絡(luò)通信的時(shí)候。比如,一個(gè)服務(wù)器可能會(huì)等待用戶請求,或者一個(gè)網(wǎng)絡(luò)應(yīng)用程序會(huì)等待遠(yuǎn)程主機(jī)的響應(yīng)。



如果你正在使用通道——在Java 1.4中可以獲得的新的I/O API——封鎖的線程將會(huì)得到一個(gè)ClosedByInterruptException異常。如果是這種情況,處理的邏輯方法與在第三個(gè)例子中所使用的相同——不同的僅僅是產(chǎn)生的異常有所區(qū)別。



但是,由于新的I/O最近才發(fā)布并且還有待進(jìn)一步研究,你也可能還在使用Java 1.0以來一直提供的傳統(tǒng)的I/O。這種情況下,使用Thread.interrupt()不會(huì)產(chǎn)生任何幫助,原因是線程將不會(huì)退出封鎖狀態(tài)。列表D中的代碼顯示了這一過程。盡管調(diào)用了interrupt()方法,線程還是沒有退出封鎖狀態(tài)。

值得慶幸的是,Java Platform提供了在這種情況下的解決方案:通過調(diào)用鎖定線程的socket的close()方法。這樣的情況下,如果線程是在進(jìn)行I/O操作時(shí)被鎖定,線程將得到一個(gè)SocketException異常,這非常類似于interrupt()方法引發(fā)了InterruptedException異常的情況。

唯一需要提醒的地方是對socket的引用必須可用,這樣的話close()方法才能被調(diào)用。這也意味著socket對象也必須被共享。列表E顯示了這種情況。處理的邏輯過程與以前給出的范例相同。

運(yùn)行列表E中代碼將會(huì)得到如下預(yù)期結(jié)果:

Starting thread...

Waiting for connection...

Asking thread to stop...

accept() failed or interrupted...

Thread exiting under request...

Stopping application...

多線程是功能非常強(qiáng)大的工具,但也給自身的處理帶來了一些列挑戰(zhàn)。其中之一便是如何中斷一個(gè)正在運(yùn)行的線程。如果處理得當(dāng),使用這些技術(shù)中斷線程的難度將不會(huì)大于使用Java Platform提供的內(nèi)建操作完成同樣的任務(wù)。

列表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()退出封鎖狀態(tài)



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..." );

}

}





標(biāo)簽:中斷Java線程 

相關(guān)文章