虽说Thread类提供了stop()和suspend()方法,但这两种方法过于粗暴,如果线程占用了一些资源(如打开了一个文件,建立了一个数据库连接什么的),直接stop()或是suspend()是会产生问题的。
要终止Thread,最好的方法就是让run()方法正常运行完毕,不过有的run()方法里面直接是一个while (true),这时就要使用一些特殊的手段。
1. 使用中断
基本思想就是在run()方法中的while (true)里检查线程是否中断,如果中断就退出(当然,退出之前可以做一些关闭资源的操作);这么一来在主线程中就可以调用Thread.interrupt()来中断线程,进而使线程退出。
public class Runner3 implements Runnable {
@Override
public void run() {
while(true) {
System.out.println(new Date());
long time = System.currentTimeMillis();
while (System.currentTimeMillis() - time < 1000) {
// 不使用Thread.sleep(1000)
// 使用while来消耗一秒钟时间
}
if (Thread.currentThread().isInterrupted()) { // 时刻检查该线程是否中断
// 或者使用 if (Thread.interrupted()) {
return; // 如果线程中断就退出
}
}
}
}
public class MultiThreadTest3 {
public static void main(String[] args) {
Runner3 r = new Runner3();
Thread t = new Thread(r);
t.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// do nothing
}
t.interrupt(); // 中断Thread t,使run()方法退出,线程结束
}
}
如果在run()方法中的while (true)里有可能导致InterruptedException的操作,那么退出run()方法的代码可以放在catch语句里。
public class Runner2 implements Runnable {
@Override
public void run() {
while(true) {
System.out.println(new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return; // 发生中断异常时,线程直接退出
}
}
}
}
public class MultiThreadTest2 {
public static void main(String[] args) {
Runner2 r = new Runner2();
Thread t = new Thread(r);
t.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// do nothing
}
t.interrupt(); // 中断Thread t,使t.sleep()时产生中断异常,进而终止线程
}
}
2. 使用标志位
使用标志位boolean flag,将run()方法中的while (true)改为while (flag)(轮询标志位),主线程中就就可以通过修改flag来退出线程。
public class Runner4 implements Runnable {
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
while(flag) {
System.out.println(new Date());
long time = System.currentTimeMillis();
while (System.currentTimeMillis() - time < 1000) {
// 不使用Thread.sleep(1000)
// 使用while来消耗一秒钟时间
}
}
}
}
public class MultiThreadTest4 {
public static void main(String[] args) {
Runner4 r = new Runner4();
Thread t = new Thread(r);
t.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// do nothing
}
r.setFlag(false); // 设置标志位,使run()方法退出,线程结束
}
}
这个方法有一个缺点:如果while (flag) {...}方法阻塞了,则flag的设置会失效。
3. 最好的方法是使用线程池
当线程不用了,就让它sleep并放进队列中,这样可以最大限度地利用资源。不过这暂时不在本文的讨论范围内。
2010-10-04 补充:
注意这里说的退出是这样的一种情况:主线程(比如说 main 方法)创建了一个 Thread t,然后想在主线程中使t退出。
文章一开始说的 stop()、suspend() 方法的问题是:主线程一句 t.stop() 或是 t.suspend() 就了事了,t 在 run() 方法中没有机会去关闭资源,不像中断或是轮询标志位的方法中,t 在 run() 方法里还握有一点主动权
2011-11-03 补充:
方法2可以使用的一个优化步骤是将标志位设置为 volatile