Skip to content
阅读进度

线程池怎么配置与拒绝任务? ​

返回 Java 与并发 目录

标准回答 ​

配置 ThreadPoolExecutor 要同时看线程数、排队上限和拒绝后的业务行为。corePoolSize 是核心线程数量,maximumPoolSize 是最大线程数量,workQueue 保存待执行任务,keepAliveTime 影响空闲线程回收,ThreadFactory 决定线程创建方式,RejectedExecutionHandler 处理无法接收的任务。

常见执行顺序是:线程数未到核心数量时创建线程执行;否则先尝试入队;队列放不下时,再尝试增加线程到最大数量;仍无法接收则拒绝。线程池关闭后提交也会被拒绝。因此用无界队列时,任务可能一直积压,不能指望把 maximumPoolSize 调大就一定解决问题。

四种常见拒绝策略要说清实际动作:AbortPolicy 抛 RejectedExecutionException;CallerRunsPolicy 在线程池未关闭时让提交者线程执行;DiscardPolicy 丢掉新任务;DiscardOldestPolicy 在线程池未关闭时移除队列头,再重试提交。后两者可能静默丢任务,不能用于默认承诺完成的重要操作。队列头也不必然代表业务上最没价值的任务。

CPU 密集任务从与可用算力相称的并发度开始验证;I/O 任务可以允许更多并发,但受连接数、内存、设备服务和超时约束。拒绝策略由任务是否可丢、提交者能否承担执行、失败如何反馈决定。界面预加载可以按需要淘汰过期任务,用户必需的文件读取则不能因为“只是读”就随意丢;主线程提交重任务时,也要避免 CallerRunsPolicy 把重活反推回主线程。

关键机制 ​

  • ThreadPoolExecutor 常见执行顺序:未达到 corePoolSize 先建线程;否则尝试入队;队列放不下再扩至 maximumPoolSize;仍无法接收则拒绝。无界队列通常会让最大线程数难以发挥作用。
  • CPU 密集型从接近可用核数的并发度开始压测;IO 型可以更多,但要受下游连接、内存和超时约束,不能无限扩容。
  • AbortPolicy 抛出拒绝异常;CallerRunsPolicy 由提交者执行;DiscardPolicy 直接丢新任务;DiscardOldestPolicy 尝试丢队列头后重试。
  • 交互刷新可按业务去重或合并;重要写入不能静默丢失。线程池隔离与有界队列可避免某类慢任务拖垮全部工作。

通俗例子 ​

头像预加载可取消已滑出屏幕的旧任务;支付或保存动作要明确反馈失败并保证可恢复。主线程提交的重任务不适合 CallerRunsPolicy,否则拥堵时反而卡住页面。

追问与回答 ​

想丢掉过期图片预加载,直接用 DiscardOldestPolicy 吗? ​

不能只看名字就决定。队列头不一定是已经离屏或过期的图片任务,静默丢弃还可能让等待方一直等不到结果。更合适的是按图片请求和页面状态标识取消过期任务,合并重复请求,并明确通知取消;是否使用通用拒绝策略,要与队列和回调契约配套。

AbortPolicy 抛异常,是不是应用就一定崩溃? ​

不一定。它在任务被拒绝时抛 RejectedExecutionException,调用方可以捕获并转成明确的失败、稍后重试或降级状态。如果完全不处理且异常一路逃逸,才可能造成相应线程或应用故障。重点是把“未受理”反馈清楚,不能让用户以为操作已进入执行。

易混淆点 ​

CPU 或 IO 类型不能单独决定丢弃策略。读取文件虽无写副作用,但若是用户必需的配置或结果,也不能随意丢。

参考资料 ​

内容来源 ​

用完整回答理解机制,用真实证据解释项目。