基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

线程池有哪些常见的拒绝策略(RejectedExecutionHandler)?分别在什么样的业务场景下使用?

知识点图片

在Java的ThreadPoolExecutor中,当核心线程池已满、任务队列已满,且最大线程池也已满时,如果还有新任务提交进来,线程池就会触发拒绝策略(RejectedExecutionHandler)

JDK原生提供了4种标准的拒绝策略,同时在实际开发中,我们也经常需要自定义拒绝策略。以下是它们的原理及适用的业务场景:


1. AbortPolicy(默认策略:中止策略)

  • 处理机制:直接抛出 RejectedExecutionException 异常,阻止系统正常运行。
  • 优点:Fail-fast(快速失败),能立刻感知到系统负载达到极限,不会默默丢弃数据。
  • 缺点:如果业务代码没有捕获该异常,可能会导致整个调用链路中断。
  • 适用场景不允许丢失任务的强一致性/核心业务场景
    • 举例:订单支付处理、核心资金转账。当系统处理不过来时,直接报错给调用方(或前端页面提示“系统繁忙”),而不是让任务下落不明。调用方捕获异常后可以进行重试或降级处理。

2. CallerRunsPolicy(调用者运行策略)

  • 处理机制:不抛出异常,也不丢弃任务,而是将任务退回给提交任务的线程(通常是主线程或Web容器的Worker线程)去直接同步执行。
  • 优点:提供了一种天然的背压(Backpressure)机制。因为提交任务的线程被拿去执行任务了,它在执行完之前无法再提交新任务,从而减缓了任务提交的速度,给线程池腾出缓冲时间。不会丢失任何任务。
  • 缺点:如果任务执行非常耗时,会严重阻塞提交任务的线程。如果是Web请求线程(如Tomcat线程)触发了此策略,会导致该Web线程阻塞,进而拖垮整个Web服务的响应速度。
  • 适用场景并发量不大、不允许丢失任务、且允许一定延迟的后台异步场景
    • 举例:后台定时任务进行大批量数据同步、生成报表。主线程慢慢处理也无所谓,只要最终所有数据都处理完即可。绝对不能用于高并发的Web请求接口中

3. DiscardPolicy(丢弃策略)

  • 处理机制:默默地丢弃掉提交的新任务,不抛出任何异常,也不做任何处理。
  • 优点:性能开销极小,完全不会影响其他任务和调用者。
  • 缺点静默失败。任务丢了你根本不知道,极难排查问题。
  • 适用场景允许任务丢失、无关紧要的非核心场景
    • 举例:普通的日志记录(非审计级别)、前端页面的非关键点击埋点异步收集。丢了一小部分数据对系统和业务最终结果影响微乎其微。

4. DiscardOldestPolicy(弃老策略)

  • 处理机制:丢弃掉任务队列中最老的一个任务(即队头马上要被执行的那个任务),然后尝试重新提交当前的新任务。
  • 优点:保证了最新提交的任务能够被尽快处理。
  • 缺点:会丢失老任务。
  • 适用场景具有时效性的场景,旧数据失去价值,只需关注最新数据
    • 举例
      1. 实时位置上报:网约车司机的GPS位置上报,如果处理不过来,丢弃前几秒的位置没关系,处理最新的位置才最有意义。
      2. 股票/行情系统的最新报价推送:旧的报价在有了新报价之后就失去了意义,可以直接丢弃。

5. 自定义拒绝策略(实战中最常用)

在企业级真实业务中,JDK自带的4种策略往往无法满足严苛的生产要求(要么丢数据,要么抛异常报错)。因此,我们通常会实现 RejectedExecutionHandler 接口来自定义拒绝策略

  • 处理机制:编写自定义逻辑,通常是降级、记录日志、保存到中间件
  • 适用场景高可靠性要求的核心业务
  • 常见自定义方案
    1. 日志告警 + 丢弃:记录 ERROR 日志,并触发邮件/钉钉/短信告警,让运维开发人员迅速介入。
    2. 持久化补偿(最常用):将拒绝的任务序列化后,保存到 数据库(如MySQL任务表)、Redis消息队列(MQ) 中。后台再启动一个定时任务(定时器),在夜间低峰期将这些“积压任务”重新捞出来投入线程池执行。
    3. Dubbo的拒绝策略(参考):Dubbo的线程池耗尽后,除了抛出异常,还会打印出详细的线程池状态(核心线程数、队列大小等),甚至会导出当前 JVM 的 Thread Dump 堆栈信息到文件中,极大地方便了事后排查。

总结与选型指南

拒绝策略 行为表现 是否丢任务 适用场景
AbortPolicy (默认) 抛出异常报错 是 (当前任务) 核心业务,必须让上游知道失败以便重试或降级
CallerRunsPolicy 让提交任务的线程自己执行 后台批量处理、数据同步(慎用于Web请求线程)
DiscardPolicy 默默丢弃当前任务 是 (当前任务) 非核心的日志、无关紧要的埋点上报
DiscardOldestPolicy 丢弃队列里排队最久的老任务 是 (老任务) 实时性要求高、旧数据无用的场景(如GPS定位、实时行情)
自定义策略 降级/入库/发MQ/记录日志 根据设计而定 生产环境中的高可靠业务,保证任务最终被执行或被追踪
00:00
00:00