在Java中有以下三种方法可以终止正在运行的线程:
1. 通过设置退出标志位,使线程正常退出。
2. 调用Thread类中的stop()方法强行终止线程。但是不推荐使用这个方法,该方法已被弃用。
3. 使用Thread类中的interrupt()方法中断线程。
一、使用标志位终止线程
在线程类中,我们会定义一个标志位表示是否需要终止线程,并提供一个公共方法供外部设置标志位,在run()方法中通过标志位的取值判断是否需要终止。
例如,我们模拟了一个服务器不断接收客户端请求的过程。当主线程中将“running”这一标志位设为false后,while死循环就会结束,线程体执行结束,整个线程也就执行完毕了。
public class Main {
public static void main(String[] args) throws InterruptedException {
// start a thread (simulated server)
Server server = new Server();
new Thread(server).start();
// run the thread for about 10 seconds
Thread.sleep(10000);
// time to stop the thread
server.setRunning(false);
}
}
class Server implements Runnable {
// whether the server should be running or not
private volatile boolean running = true;
// setter method for user
public void setRunning(boolean running) { this.running = running; }
@Override
public void run() {
// run until the flag is set to be false
while (running) {
// simulate the process of server to handle the request
System.out.println("Server is processing the request...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// finish the process and stop the thread
System.out.println("Server is closing...");
}
}
二、调用stop()方法终止线程
通过调用Thread类中的stop()方法可以终止一个线程的运行,但是该方法已经在JDK中被声明为已过时(deprecated),不再推荐使用,因为该方法被认为是不安全的。具体的原因有:
1. 调用stop()方法会立刻停止run()方法中剩余的全部工作,包括在catch或finally语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
2. 调用stop()方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。
例如,存在一个对象user持有ID和NAME两个字段,假如写入线程在写对象的过程中,只完成了对ID的赋值,但没来得及为NAME赋值,就被stop()导致锁被释放,那么当读取线程得到锁之后再去读取对象user的ID和Name时,就会出现数据不一致的问题。
三、调用interrupt()方法终止线程
通过调用Thread类中的interrupt()方法也可以终止线程,但是它和stop()还是有着明显的区别。事实上,interrupt()方法并不会立即执行中断操作,而只是给线程设置一个为true的中断标志,所以它并不是真正的终止线程(否则那和stop()方法有什么区别了呢)。
来看下面一个例子:
public class Main {
public static void main(String[] args) throws InterruptedException {
// start a thread
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
// wait for 5ms
Thread.sleep(5);
// now we interrupt the thread
thread.interrupt();
}
}
class Task implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("In Thread: " + i);
}
}
}
上述程序的输出结果如下:
In Thread: 0
In Thread: 1
In Thread: 2
In Thread: 3
In Thread: 4
In Thread: 5
............
In Thread: 9995
In Thread: 9996
In Thread: 9997
In Thread: 9998
In Thread: 9999
我们惊讶的发现,线程体居然顺利的全部执行完了,设置的中断完全没有起到作用。为了让线程能够中断,我们需要在run()方法中使用isInterrupted()方法进行判断:如果希望线程在中断后停止,就必须先判断是否被中断,并为它增加相应的中断处理代码。
public class Main {
public static void main(String[] args) throws InterruptedException {
// start a thread
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
// wait for 5ms
Thread.sleep(5);
// now we interrupt the thread
thread.interrupt();
}
}
class Task implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
// check whether the thread is interrupted
if (Thread.currentThread().isInterrupted())
// if true, break the loop
break;
System.out.println("In Thread: " + i);
}
}
}
修改后程序的运行结果如下:
In Thread: 0
In Thread: 1
In Thread: 2
In Thread: 3
.............
In Thread: 207
In Thread: 208
In Thread: 209
In Thread: 210
In Thread: 211
In Thread: 212
显然,线程体远未执行完毕就被中断提前结束了。
需要注意的是,
1. 如果,线程的当前状态处于非阻塞状态,那么仅仅是线程的中断标志被修改为true而已;
2. 如果线程的当前状态处于阻塞状态,那么在将中断标志设置为true后,如果是wait、sleep以及join三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,并抛出一个InterruptedException,这样受阻线程就得以退出阻塞的状态。