1、任务取消
调用interrupt并不意味着必然停止目标线程正在进行的工作;它仅仅传递了请求中断的消息。
静态的interrupt应该小心使用,因为它会清除并发线程的中断状态。
中断是实现取消最明智的选择。
class PrimeProducer extends Thread{ private final BlockingQueue queue; PrimeProducer(BlockingQueue queue){ this.queue = queue; } public void run(){ try{ BigInteger p = BigInteger.ONE; while(!Thread.currentThread().isInterrupt()) queue.put(p = p.nextProbablePrime()); }catch(InterruptedException consumed){ /*允许线程退出*/ } } }
因为每一个线程都有其自己的中断策略,所以你不应该中断线程,除非你知道中断这个线程意味着什么。
只有实现了线程中断策略的代码才可以接收中断请求。通过目的的任务和库的代码绝对不应该接收中断请求。
通过Future来取消任务:
public static void timeRun(Runnable r,long timeout,TimeUnit unit)throws InterruptedException{ Future task = taskExec.submit(r); try{ task.get(timeout,unit); }catch(TimeoutException e){ //下面任务会被取消 }catch(){ //task中抛出的异常;重抛出 throw launderThrowable(e.getCause()); }finally{ //如果任务已经结束,是无害的 task.cancle(true); //interrupt if running } }
线程阻塞,不可中断:java.io中的同步Socket I/O;java.nio中的同步I/O;Selector的异步I/O;获得锁。
用newTaskFor封装非标准取消。
2、停止基于线程的服务
ExecutorService提供了shutdown和shutdownNow方法,其他线程持有的服务也应该提供类似的关闭机制。
对于线程持有的服务,只要服务的存在时间大于创建线程的方法存在时间,那么就应该提供生命周期方法。
3、处理反常的线程终止
在一个长时间运行的应用程序中,所有的线程都要给未捕获异常设置一个处理器,这个处理器至少要将异常信息记入日志中。
4、关闭JVM
线程分为两种:普通线程和精灵线程。JVM启动时创建的所有线程,除了主线程以外,其它都是精灵线程(比如垃圾回收器和其它类型线程)。
应用程序中,精灵线程不能替代服务对生命周期恰当、良好的管理。
使用finally块和显式close方法的结合来管理资源,会比使用finalizer起到更好的作用。避免使用finalizer。
使用FutureTask和Executor框架可以简化构建可取消的任务和服务。