实现并发异常自动报警需嵌入线程池生命周期,从四方面入手:1. 自定义ThreadFactory绑定异常处理器;2. 重写afterExecute统一捕获异常;3. 包装拒绝策略暴露拒绝上下文;4. 动态水位监控联动多维阈值告警。

要实现在并发任务处理异常时的变量自动报警,关键不是等异常发生后再“捞日志”,而是把监控逻辑嵌入线程池生命周期,并对异常、拒绝、积压三类风险点做主动捕获与分级告警。下面从四个实用维度展开。
用自定义ThreadFactory绑定异常处理器
这是捕获 Runnable 任务中未处理异常 的最直接方式。默认 execute() 提交的任务一旦抛出 RuntimeException,线程会静默终止,无日志、无上报。
- 为每个线程设置
UncaughtExceptionHandler,打印线程名、堆栈、时间戳,并触发告警(如发钉钉/企业微信) - 线程名必须带业务标识(如
"order-processor-pool-1"),否则多个线程池日志混在一起无法定位 - 避免使用
Executors.defaultThreadFactory(),务必手写或用 Lombok + 前缀工厂,确保线程可追溯
重写 afterExecute() 统一兜底异常处理
比 ThreadFactory 更进一步:它能捕获 所有任务执行结束后的异常状态,包括 submit() 返回 Future 后 get() 抛出的 ExecutionException。
- 继承
ThreadPoolExecutor,重写afterExecute(Runnable r, Throwable t) - 若
t != null,说明任务执行中抛了未捕获异常;若r instanceof Future且isDone()为 true,可调用get()检查是否封装异常 - 在此处记录指标(如异常计数原子递增)、打结构化日志(含 traceId、poolName、taskClass)、并触发阈值告警(例如 1 分钟内异常 ≥ 5 次)
包装拒绝策略,暴露 reject 计数与上下文
任务被拒绝不等于“没发生”,尤其用 DiscardPolicy 时,异常完全静默丢失。必须让拒绝行为可观测、可告警。
- 不要直接 new ThreadPoolExecutor(..., new AbortPolicy()),而是用代理包装:如
RejectedProxyUtil.createProxy(handler, "pay-pool", rejectCounter) - 在代理 handler 中,除了调用原策略,还要:
– 原子递增拒绝计数
– 记录被拒任务的提交时间、类型、参数摘要(脱敏)
– 若 30 秒内拒绝数 > 10,立即触发 P1 级告警 - 配合监控
getRejectedExecutionCount(),注意该方法需通过自定义 Executor 或 DynamicTp 暴露,标准 JDK 实现不提供 public 访问
动态水位监控 + 多维阈值联动告警
异常报警不能孤立存在——它必须和线程池饱和趋势联动,才能区分是偶发错误还是系统性过载。
- 每 10 秒采集一次:
– 队列 size(有界队列算 %,无界队列看绝对值 >1000)
– 活跃线程数 / 最大线程数 ≥ 90%
– 过去 60 秒平均完成任务数
– 当前队列长度 ÷ 平均完成速率 > 5 秒(意味着积压清空延迟超标) - 满足任一条件即标记“高风险”,连续 3 次则触发告警;若同时出现拒绝计数上升 + 异常率升高,升级为“严重过载”告警
- 告警消息必须含可操作字段:当前 poolName、queue.size、activeCount、rejectCount、lastRejectTaskType,避免只说“线程池异常”却不知改哪

















