大促线程池需协同设计workQueue容量、corePoolSize/maxPoolSize比例及拒绝策略:用有界ArrayBlockingQueue替代无界队列,按QPS×RT×1.2~1.5设容量;CPU密集型设core=核数+1,IO密集型设core=2×核数;拒绝策略推荐CallerRunsPolicy或自定义降级,禁用静默丢弃策略,并配套监控告警与动态调参。

大促期间流量突增,线程池若配置不当,极易因任务在队列中持续堆积导致堆内存耗尽(OOM)。关键不是“压不压得住”,而是让线程池在资源可控前提下,把压力合理分流、缓冲、降级——核心在于 workQueue 容量、corePoolSize/maxPoolSize 比例、拒绝策略联动 三者协同设计。
用有界队列替代无界队列,明确容量上限
LinkedBlockingQueue 默认无界(容量 Integer.MAX_VALUE)是大促 OOM 的头号诱因。任务处理慢 + 提交快 → 队列无限膨胀 → Full GC 频发 → OOM。必须显式设限:
- 优先选 ArrayBlockingQueue,构造时强制指定容量,例如
new ArrayBlockingQueue(200) - 容量值参考:按峰值 QPS × 平均响应时间(秒)× 安全系数 1.2~1.5 估算,如 QPS=500、RT=0.2s → 基础缓冲 ≈ 100 → 设为 150~200
- 避免 SynchronousQueue 单独使用:它不缓存任务,所有提交都立即触发扩容,高并发下线程数飙升,CPU 和栈内存压力剧增
按业务类型设置合理的线程数区间
线程数不是越多越好,要匹配实际负载特征:
- CPU 密集型任务(如商品价格实时计算、风控规则引擎):corePoolSize = CPU 核心数 + 1,maximumPoolSize 可设为 corePoolSize × 1.2~1.5,避免频繁上下文切换
- IO 密集型任务(如调用下游库存、优惠券、用户中心接口):corePoolSize = 2 × CPU 核心数,maximumPoolSize 可设为 corePoolSize × 2,预留弹性应对网络抖动
- 无论哪种类型,maximumPoolSize 必须小于系统可承受的线程总数(如 Linux ulimit -u 限制),建议不超过 200,防止创建过多线程耗尽栈内存或触发 OOM Killer
拒绝策略必须配合业务降级,不能只抛异常
当队列满 + 线程数已达 maximumPoolSize,拒绝策略就是最后一道防线。AbortPolicy 直接抛 RejectedExecutionException 会中断主链路,引发雪崩:
- 推荐 CallerRunsPolicy:由提交线程(如 Tomcat worker 线程)同步执行任务,天然限流,且不丢失请求;适合非核心但需保底的任务(如日志上报、异步通知)
- 对关键路径(如下单、支付),应自定义拒绝策略,例如:将任务写入 Kafka 或 Redis 延迟队列,触发告警并降级为“稍后重试”提示
- 禁用 DiscardPolicy 和 DiscardOldestPolicy:静默丢弃无感知,线上难以定位问题;丢弃最老任务可能丢掉已排队成功的订单
配套监控与动态响应机制
参数设好只是起点,大促中必须可观测、可干预:
- 暴露关键指标:队列长度(
executor.getQueue().size())、活跃线程数(executor.getActiveCount())、已完成任务数(executor.getCompletedTaskCount()) - 设置告警阈值:队列使用率 > 80%、活跃线程数 ≥ maximumPoolSize × 0.9 时立即告警
- 支持运行时调参(如通过 Apollo/Nacos):紧急情况下可临时调大队列容量或 corePoolSize,避免重启服务

















