1 minute read

虽说 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() 就了事了,trun() 方法中没有机会去关闭资源,不像中断或是轮询标志位的方法中,trun() 方法里还握有一点主动权

2011-11-03补充

方法 2 可以使用的一个优化步骤是将标志位设置为 volatile

Comments