可通过继承ThreadPoolExecutor重写beforeExecute和afterExecute方法精准监控任务耗时:beforeExecute用System.nanoTime()记录开始时间并存入ConcurrentHashMap,afterExecute计算纳秒差并转毫秒,覆盖正常与异常执行场景,支持日志、指标上报及超时告警。

在 Java 线程池中,可以通过继承 ThreadPoolExecutor 并重写 beforeExecute 和 afterExecute 方法,实现对每个任务执行耗时的精准监控。
重写 beforeExecute 记录开始时间
该方法在线程即将执行任务前被调用,适合存入当前系统纳秒时间戳(System.nanoTime()),避免毫秒级精度不足。注意不能用 System.currentTimeMillis(),因它受系统时钟调整影响,可能导致负耗时。
- 将时间戳与任务对象关联,推荐使用
ThreadLocal或ConcurrentHashMap缓存(后者需注意 key 的唯一性,如用task.hashCode() + System.identityHashCode(task)组合) - 若任务可能被复用(如
Runnable实现类被多次提交),应确保每次提交生成独立标识,避免时间戳污染
重写 afterExecute 获取耗时并上报
该方法在任务执行完毕(无论正常结束或抛出异常)后被调用,是计算耗时和清理记录的合适位置。
- 从缓存中取出对应开始时间,用
System.nanoTime() - startNanos得到纳秒级耗时,再转为毫秒(除以 1_000_000.0) - 可在此处打印日志、上报 Prometheus 指标、写入监控队列,或触发超时告警(如耗时 > 5000ms)
- 务必捕获并处理缓存未命中情况(例如任务被拒绝、线程池提前 shutdown),避免 NPE 或统计偏差
注意异常任务的完整覆盖
afterExecute 会接收两个参数:Runnable r 和 Throwable t。当任务抛出未捕获异常时,t 非 null;若任务正常完成,t 为 null。但即使发生异常,也要统计其实际执行时间——这正是钩子方法比 AOP 更可靠的原因。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 不要仅在
t == null时才计算耗时 - 可额外记录异常类型(
t.getClass().getSimpleName())和消息摘要,便于根因分析 - 若使用
FutureTask或Callable,需注意beforeExecute接收的是包装后的FutureTask,不是原始Callable,但耗时统计逻辑不变
一个轻量可用的示例实现
无需引入外部依赖,只需扩展 ThreadPoolExecutor:
public class TimingThreadPoolExecutor extends ThreadPoolExecutor {
private final ConcurrentHashMap<Object, Long> startTimeMap = new ConcurrentHashMap<>();
<pre class='brush:java;toolbar:false;'>public TimingThreadPoolExecutor(int corePoolSize, int maxPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
startTimeMap.put(r, System.nanoTime());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Long start = startTimeMap.remove(r);
if (start != null) {
long duration = System.nanoTime() - start;
double ms = duration / 1_000_000.0;
String msg = String.format("Task %s took %.2f ms", r, ms);
if (t != null) {
msg += " — failed with " + t.getClass().getSimpleName();
}
System.out.println(msg); // 替换为你的日志框架或监控上报
}
}}

















