扩展ThreadPoolExecutor
ThreadPoolExecutor是可扩展的,通过查看源码可以发现,它提供了几个可以在子类化中改写的方法:beforeExecute,afterExecute,terminated。源码片段如下所示:
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
可以注意到,这三个方法都是protected的空方法,摆明了是让子类扩展的嘛。
在执行任务的线程中将调用beforeExecute和afterExecute等方法,在这些方法中还可以添加日志、计时、监视或者统计信息收集的功能。无论任务是从run中正常返回,还是抛出一个异常而返回,afterExecute都会被调用。如果任务在完成后带有一个Error,那么就不会调用afterExecute。如果beforeExecute抛出一个RuntimeException,那么任务将不被执行,并且afterExecute也不会被调用。
在线程池完成关闭时调用terminated,也就是在所有任务都已经完成并且所有工作者线程也已经关闭后,terminated可以用来释放Executor在其生命周期里分配的各种资源,此外还可以执行发送通知、记录日志或者收集finalize统计信息等操作。
下面就以给线程池添加统计信息为例:
public class TimingThreadPool extends ThreadPoolExecutor {
private final ThreadLocal startTime = new ThreadLocal();
private final Logger log = Logger.getAnonymousLogger();
private final AtomicLong numTasks = new AtomicLong();
private final AtomicLong totalTime = new AtomicLong();
public TimingThreadPool(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
log.info(String.format("Thread %s: start %s", t, r));
startTime.set(System.nanoTime());
}
protected void afterExecute(Runnable r, Throwable t) {
try {
long endTime = System.nanoTime();
long taskTime = endTime - startTime.get();
numTasks.incrementAndGet();
totalTime.addAndGet(taskTime);
log.info(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();
}
}
}
可以看到TimingThreadPool重写了父类的三个方法。
下面写一个测试类,参考运行效果:
public class CheckTimingThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor exec = new TimingThreadPool(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS, new SynchronousQueue());
exec.execute(new DoSomething(5));
exec.execute(new DoSomething(4));
exec.execute(new DoSomething(3));
exec.execute(new DoSomething(2));
exec.execute(new DoSomething(1));
exec.shutdown();
}
}
class DoSomething implements Runnable {
private int sleepTime;
public DoSomething(int sleepTime) {
this.sleepTime = sleepTime;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running.");
try {
TimeUnit.SECONDS.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
一月 20, 2018 5:34:51 下午 concurrent.TimingThreadPool beforeExecute
INFO: Thread Thread[pool-1-thread-5,5,main]: start concurrent.DoSomething@63425e17
pool-1-thread-5 is running.
一月 20, 2018 5:34:51 下午 concurrent.TimingThreadPool beforeExecute
INFO: Thread Thread[pool-1-thread-2,5,main]: start concurrent.DoSomething@156237e0
pool-1-thread-2 is running.
一月 20, 2018 5:34:51 下午 concurrent.TimingThreadPool beforeExecute
INFO: Thread Thread[pool-1-thread-3,5,main]: start concurrent.DoSomething@156237e0
pool-1-thread-3 is running.
一月 20, 2018 5:34:51 下午 concurrent.TimingThreadPool beforeExecute
INFO: Thread Thread[pool-1-thread-1,5,main]: start concurrent.DoSomething@156237e0
pool-1-thread-1 is running.
一月 20, 2018 5:34:51 下午 concurrent.TimingThreadPool beforeExecute
INFO: Thread Thread[pool-1-thread-4,5,main]: start concurrent.DoSomething@1a2f6aea
pool-1-thread-4 is running.
一月 20, 2018 5:34:52 下午 concurrent.TimingThreadPool afterExecute
INFO: Thread null: end concurrent.DoSomething@63425e17, time=1000557327ns
一月 20, 2018 5:34:53 下午 concurrent.TimingThreadPool afterExecute
INFO: Thread null: end concurrent.DoSomething@1a2f6aea, time=1999474354ns
一月 20, 2018 5:34:54 下午 concurrent.TimingThreadPool afterExecute
INFO: Thread null: end concurrent.DoSomething@156237e0, time=2999558243ns
一月 20, 2018 5:34:55 下午 concurrent.TimingThreadPool afterExecute
INFO: Thread null: end concurrent.DoSomething@156237e0, time=3999593049ns
一月 20, 2018 5:34:56 下午 concurrent.TimingThreadPool afterExecute
INFO: Thread null: end concurrent.DoSomething@156237e0, time=4999608220ns
一月 20, 2018 5:34:56 下午 concurrent.TimingThreadPool terminated
INFO: Terminated: avg time=2999758238ns
可以看到,在测试类CheckTimingThreadPool中通过execute了五个线程,然后分别对这五个线程进行统计,最后统计出各个线程的耗时平均时间。