ThreadPoolExecutor是可扩展的,它提供了几个可以在子类化中改写的方法:beforeExecute、afterExecute 和terminated,这些方法可以用于扩展ThreadPoolExecutor的行为。
在执行任务的线程中将调用beforeExecute和afterExecute等方法,在这些方法中还可以添加日志、计时、监视或统计信息收集的功能。无论任务是从run 中正常返回,还是抛出一个异常而返回,afterExecute都会被调用。(如果任务在完成后带有一个Error,那么就不会调用afterExecute。)如果beforeExecute 抛出一个RuntimeException,那么任务将不被执行,并且afterExecute也不会被调用。
在线程池完成关闭操作时调用terminated,也就是在所有任务都已经完成并且所有工作者线程也已经关闭后。terminated可以用来释放Executor 在其生命周期里分配的各种资源,此外还可以执行发送通知、记录日志或者收集finalize 统计信息等操作。
示例:给线程池添加统计信息
在程序清单8-9 的TimingThreadPool 中给出了一个自定义的线程池,它通过beforeExecute、afterExecute 和terminated 等方法来添加日志记录和统计信息收集。为了测量任务的运行时间,beforeExecute 必须记录开始时间并把它保存到一个afterExecute可以访问的地方。因为这些方法将在执行任务的线程中调用,因此beforeExecute可以把值保存到一个ThreadLocal变量中,然后由afterExecute来读取。在TimingThreadPool中使用了两个AtomicLong 变量,分别用于记录已处理的任务数和总的处理时间,并通过terminated来输出包含平均任务时间的日志消息。
程序清单8-9增加了日志和计时等功能的线程池
public class TimingThreadPool extends ThreadPoolExecutor {
private final ThreadLocal<Long>startTime
=new ThreadLocal<Long>();
private final Logger log =Logger. getLogger("TimingThreadPool");
private final AtomicLong numTasks =new AtomicLong();
private final AtomicLong totalTime =new AtomicLong();
protected void beforeExecute(Thread t, Runnable r){
super. beforeExecute(t,r);
log. fine(String. format("Thread %s:start %s",t,r));
protected void afterExecute(Runnable r, Throwable t){
try {
long endTime =System. nanoTime();
long taskTime =endTime -startTime. get();
numTasks. incrementAndGet();
totalTime'. addAndGet(taskTime);
log. fine(String. format("Thread %s:end %s, time=%dns",
t,r,taskTime) ) ;
}finally {
super. afterExecute(r,t);
}
}
protected void terminated(){
try {
log.info (String. format("Terminated:avg time=%dns",
totalTime. get ( ) / numTasks. get ( ) ) ) ;
}finally {
super. terminated();
}
}
}