簡介
在Java的多線程編程中,java.lang.Thread類型包含了一些列的方法start(), stop(), stop(Throwable) and suspend(), destroy() and resume()。通過這些方法,我們可以對線 程進行方便的操作,但是這些方法中,只有start()方法得到了保留。
在Sun公司的一篇文章《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 》中詳細講解了捨棄這些方法的原因。那麼,我們究竟應該如何停止線程呢?
建議使用的方法
在《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 》中,建 議使用如下的方法來停止線程:
private volatile Thread blinker;
public void stop() {
blinker = null;
}
public void run() {
Thread thisThread = Thread.currentThread();
while (blinker == thisThread) {
try {
thisThread.sleep(interval);
} catch (InterruptedException e){
}
repaint();
}
}
關於使用volatile關鍵字的原因,請查看 http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930。
當線程處於非運行(Run)狀態
當線程處於下面的狀況時,屬於非運行狀態:
* 當sleep方法被調用。
*當wait方法被調用。
*當被I/O阻塞,可能是文件或者網絡等等。
當線程處於上述的狀態時,使用前面介紹的方法就不可用了。這個時候,我們可以使用 interrupt()來打破阻塞的情況,如:
public void stop() {
Thread tmpBlinker = blinker;
blinker = null;
if (tmpBlinker != null) {
tmpBlinker.interrupt();
}
}
當interrupt()被調用的時候,InterruptedException將被拋出,所以你可以再run方法中 捕獲這個異常,讓線程安全退出:
try {
....
wait();
} catch (InterruptedException iex) {
throw new RuntimeException("Interrupted",iex);
}
阻塞的I/O
當線程被I/O阻塞的時候,調用interrupt()的情況是依賴與實際運行的平台的。在 Solaris和Linux平台上將會拋出InterruptedIOException的異常,但是Windows上面不會有這 種異常。所以,我們處理這種問題不能依靠於平台的實現。如:
package com.cnblogs.gpcuster
import java.net.*;
import java.io.*;
public abstract class InterruptibleReader extends Thread {
private Object lock = new Object( );
private InputStream is;
private boolean done;
private int buflen;
protected void processData(byte[] b, int n) { }
class ReaderClass extends Thread {
public void run( ) {
byte[] b = new byte[buflen];
while (!done) {
try {
int n = is.read(b, 0, buflen);
processData(b, n);
} catch (IOException ioe) {
done = true;
}
}
synchronized(lock) {
lock.notify( );
}
}
}
public InterruptibleReader(InputStream is) {
this(is, 512);
}
public InterruptibleReader(InputStream is, int len) {
this.is = is;
buflen = len;
}
public void run( ) {
ReaderClass rc = new ReaderClass( );
synchronized(lock) {
rc.start( );
while (!done) {
try {
lock.wait( );
} catch (InterruptedException ie) {
done = true;
rc.interrupt( );
try {
is.close( );
} catch (IOException ioe) {}
}
}
}
}
}
另外,我們也可以使用InterruptibleChannel接口。 實現了InterruptibleChannel接口 的類可以在阻塞的時候拋出ClosedByInterruptException。如:
package com.cnblogs.gpcuster
import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.Channels;
public class InterruptInput {
static BufferedReader in = new BufferedReader(
new InputStreamReader(
Channels.newInputStream(
(new FileInputStream(FileDescriptor.in)).getChannel ())));
public static void main(String args[]) {
try {
System.out.println("Enter lines of input (user ctrl+Z Enter to terminate):");
System.out.println("(Input thread will be interrupted in 10 sec.)");
// interrupt input in 10 sec
(new TimeOut()).start();
String line = null;
while ((line = in.readLine()) != null) {
System.out.println("Read line:'"+line+"'");
}
} catch (Exception ex) {
System.out.println(ex.toString()); // printStackTrace();
}
}
public static class TimeOut extends Thread {
int sleepTime = 10000;
Thread threadToInterrupt = null;
public TimeOut() {
// interrupt thread that creates this TimeOut.
threadToInterrupt = Thread.currentThread();
setDaemon(true);
}
public void run() {
try {
sleep(10000); // wait 10 sec
} catch(InterruptedException ex) {/*ignore*/}
threadToInterrupt.interrupt();
}
}
}
這裡還需要注意一點,當線程處於寫文件的狀態時,調用interrupt()不會中斷線程。