中断一个线程意味着还未完成线程的任务之前结束该线程,有效地终止当前的操作。该线程是否死亡、或等待新的任务,或执行下一步依赖于当前应用。
首先,不要使用Thread.stop()方法。它确实能终止一个运行的线程,但是这个方法是不安全的并且是过期的(deprecated),意味着在未来的java版本中不可用。
另一个方法令人困惑的方法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 ) {
}
}
}
}
输出
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()终止该线程,调用Thread.sleep()方法给线程足够的时间初始化和终止,线程本身并未做其他事情。尽管调用了Thread.interrupt()方法,但是线程还是运行了一会。
最好的并且推荐使用一个共享变量终止线程,该共享变量用来通知线程必须终止当前所做的任务。该线程周期性地检查该变量,特别是在冗长的操作期间,有序地终止当前的任务。代码如下:
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..." );
}
}
输出:
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Asking thread to stop...
Thread exiting under request...
Stopping application...
尽管上面的方法需要一些额外的代码,但是它不难实现,并且给线程进行必要清理的机会,能够完全满足多线程应用的要求。只需要声明一个volatile的共享变量,然后将它封装在同步代码块或者方法中。
但是,如果线程由于等待某个事件被阻塞,阻塞的线程是无法检查共享变量,终止自己。这些场景会经常遇到,如Object.wait(),ServerSocket.accept()和 DatagramSocket.receive()等等。这些方法会一直阻塞线程,尽管使用超时机制,等待时间过期并不可行,所以必须使用提前退出阻塞状态的机制。
在Example1中,Thread.interrupt()方法并没有中断运行的线程,当线程处于阻塞状态,该方法会抛出中断异常,线程会退出阻塞状态。具体来讲,当线程被阻塞在Object.wait,Thread.join,或者Thread.sleep时,线程会接受InterruptedException1,提前终止阻塞的方法。
所以,如果一个线程被上面的方法阻塞,正确结束它的方式是设置共享变量,并调用interrupt()方法(注意,先设置共享变量)。如果线程没有被阻塞,调用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..." );
}
}
输出:
Starting thread...
Thread running...
Thread running...
Thread running...
Asking thread to stop...
Thread interrupted...
Thread exiting under request...
Stopping application...
如果线程碰到阻塞的I/O操作,I/O操作会阻塞一个线程相当多的时间,特别是网络通信的参与。这时候Thread.interrupt()方法不会让线程退出阻塞状态。代码如下:
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..." );
}
}
}
}
尽管调用了interrupt()方法,但是线程不会退出阻塞状态。
不过java平台提供了一种技术——调用socket的close()方法。在这种情形下,如果线程被I/O操作阻塞,会收到SocketException异常,类似于InterruptedException。唯一需要注意的是socket应用可用,即socket对象是共享的
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..." );
}
}
输出:
Starting thread...
Waiting for connection...
Asking thread to stop...
accept() failed or interrupted...
Thread exiting under request...
Stopping application...
参考资料:
1. Adding Multithreading Capability to Your Java Applications
2. InterruptedException and interrupting threads explained
- InterruptedException的处理方式:
- 吞并异常。只要你能确定InterruptedException不会再出现(Are you sure? Really?);
- 设置中断标识位。这是你的代码不知道该怎么处理它,但是线程还在做其他的事情,该线程同样也会对InterruptedException感兴趣,Thread.currentThread().interrupt()
;
- 传递异常。这是你的方法不知道该怎么处理,也许调用知道,这是最好的处理方式;
- 处理异常。也许当前的线程想停止,当接收到InterruptedException。
- 封装成其他异常。 ↩