自定义拒绝策略应记录告警日志并抛出业务异常:① 使用参数化日志记录线程池名、活跃线程数、队列大小等上下文;② 抛出如ServiceBusyException等带业务语义的异常;③ 确保线程安全与日志轻量,异步处理监控上报;④ 构建线程池时传入专属策略实例,并配合监控与容量治理。

自定义拒绝策略:记录告警日志并抛出业务异常
Java 线程池默认的拒绝策略(如 AbortPolicy)在任务被拒时直接抛出 RejectedExecutionException,但这个异常是运行时异常且不带业务语义。实际场景中,你往往需要:① 记录可追踪的告警日志(含上下文,如线程池名、队列大小、拒绝原因);② 抛出一个带业务含义的受检或非受检异常(如 ServiceBusyException),让上层能明确识别并做降级/重试等处理。
实现方式:继承 RejectedExecutionHandler
最直接的方式是实现 RejectedExecutionHandler 接口,重写 rejectedExecution 方法,在其中完成日志记录和异常抛出:
- 使用 SLF4J 或 Logback 记录 WARN 或 ERROR 级别日志,包含关键信息:线程池名称(建议命名)、当前活跃线程数、队列剩余容量、提交的任务类型(可选)、时间戳
- 不要直接 throw new RuntimeException(...),而是抛出你定义的业务异常类,例如:
throw new ServiceBusyException("Task rejected: pool exhausted or queue full"); - 确保该异常类在调用链上游能被捕获并合理处理(比如 API 层返回 503 或触发熔断)
注意线程安全与日志性能
拒绝策略方法会在工作线程或提交线程中被调用,本身无同步保障。日志框架(如 Logback)通常是线程安全的,但要避免在拒绝逻辑里做耗时操作(如远程调用、文件写入、复杂对象序列化):
- 日志内容尽量轻量,用参数化日志(
logger.warn("Reject task in {}, active={}, queueSize={}", poolName, pool.getActiveCount(), pool.getQueue().size());) - 避免在拒绝策略中 new 大对象或触发 GC 压力大的操作
- 若需上报监控(如 Prometheus Counter 或发送告警消息),建议异步委托给专门的告警模块,不要阻塞拒绝路径
集成到线程池的正确姿势
自定义策略需在构建线程池时传入,推荐使用 ThreadPoolExecutor 构造函数或 Executors.newThreadPoolExecutor(...)(JDK 21+):
立即学习“Java免费学习笔记(深入)”;
- 给线程池起有意义的名字(通过
ThreadFactory),方便日志中区分不同业务池 - 示例:
new ThreadPoolExecutor(core, max, keepAlive, TimeUnit.SECONDS, queue, threadFactory, new LoggingRejectPolicy("order-process-pool")); - 不要复用同一个拒绝策略实例给多个线程池,除非策略内部不依赖实例状态;否则建议按池构造专属策略实例
补充建议:配合监控与容量治理
仅记录日志和抛异常还不够。真实线上问题往往需要闭环:
- 将拒绝次数作为指标暴露给 Prometheus / Grafana,设置阈值告警
- 定期分析拒绝日志,看是突发流量、慢任务堆积,还是线程池配置不合理(如队列过大掩盖了资源瓶颈)
- 在业务入口加限流(如 Sentinel / Resilience4j),比等到线程池拒绝更前置地保护系统


















