Java线程池防阻塞关键在于让排队和拒绝可控可预期,需用有界队列、合理设线程数、选合适拒绝策略,并配合监控与动态调优。

用有界队列代替无界队列
默认的 LinkedBlockingQueue 容量是 Integer.MAX_VALUE,等于没设限。任务持续涌入而消费变慢时,队列无限膨胀,直接吃光堆内存。
- 显式指定容量,例如
new LinkedBlockingQueue(1000)或更稳妥的ArrayBlockingQueue(512) - 容量值需结合业务平均耗时、QPS、JVM 堆大小综合估算,避免过小导致频繁拒绝,过大又失去保护作用
- 切忌使用
new LinkedBlockingQueue()无参构造——这是生产环境常见隐患点
合理设置核心与最大线程数
线程不是越多越好。盲目调大 maximumPoolSize 可能触发上下文切换风暴;只靠 corePoolSize 又无法应对突发流量。
- 核心线程数建议设为 CPU 核心数 × (1~2),适合稳定负载场景
- 最大线程数应略高于核心值(如 core=8,max=16),并配合有界队列,确保“队列满 → 开新线程 → 满额 → 触发拒绝”路径清晰
- 避免使用
Executors.newCachedThreadPool(),其maximumPoolSize = Integer.MAX_VALUE+SynchronousQueue组合极易造成线程雪崩
选用合适的拒绝策略
拒绝不是失败,而是系统在压力下的主动防御。默认的 AbortPolicy 抛异常,对上游不友好;静默丢弃也不利于问题定位。
-
CallerRunsPolicy:由提交线程自己执行任务,天然限流,适合低吞吐但强一致场景 -
DiscardOldestPolicy:丢弃队列头部最老任务,适合实时性要求高、旧任务已失效的场景(如行情推送) - 自定义策略:记录日志 + 上报监控 + 返回降级响应(如“服务繁忙,请稍后重试”),让调用方感知并配合降级
配合监控与动态调优
参数不是设完就一劳永逸。线程池运行状态必须可观测,才能及时发现隐患。
立即学习“Java免费学习笔记(深入)”;
- 暴露关键指标:活跃线程数、队列长度、已拒绝任务数、任务平均执行时长
- 设置告警阈值:例如队列使用率 > 80% 持续 1 分钟,或拒绝率 > 0.1% 立即通知
- 支持运行时调整(谨慎):通过
setCorePoolSize/setMaximumPoolSize动态扩容,但需配套验证机制


















