根本对策是使用有界队列并协同设计线程数与拒绝策略:显式指定 arrayblockingqueue(256) 等固定容量队列,cpu密集型设线程数为核数+1、io密集型设2~4倍核数,配合callerrunspolicy实现背压,并添加命名线程工厂和队列/活跃线程监控。

直接用 Executors 工具类创建线程池,最容易踩的坑就是无界队列引发 OutOfMemoryError。根本对策不是“少提交任务”,而是让队列容量可预期、可控制、有兜底。
必须显式使用有界队列
默认的 LinkedBlockingQueue() 构造函数内部容量是 Integer.MAX_VALUE,等于无限缓存——任务持续涌入时,队列会无节制膨胀,迅速吃光堆内存。
- 正确写法:用
new LinkedBlockingQueue(256)或更推荐new ArrayBlockingQueue(256) -
ArrayBlockingQueue是固定数组实现,内存占用确定、不可扩容,从底层杜绝悄悄膨胀 - 容量要结合单任务内存估算:比如每个任务携带对象约 8KB,256 个 ≈ 2MB;设成 10000 就接近 80MB,风险陡增
线程数与队列容量要协同设计
只设队列大小不够,还得匹配线程的实际处理能力。否则要么线程忙不过来,要么队列早早堆满。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- CPU 密集型任务:核心线程数建议设为
Runtime.getRuntime().availableProcessors() + 1 - IO 密集型任务:可设为核数 × 2 ~ 4(如 4 核 × 3 = 12)
- 避免“8 线程 + 10000 队列”这种组合——这不是缓解压力,是把 OOM 延后发生
- 例如:核心/最大线程数都设为 8,队列设 256,则系统最多缓冲 256 个待执行任务;超出立即触发拒绝策略
配好拒绝策略,让背压真正生效
队列满了不能卡死、不能静默丢任务,拒绝策略是反压的第一道防线。
- 首选
CallerRunsPolicy:由提交任务的线程自己执行,天然拖慢上游提交速率,形成负反馈 - 慎用
AbortPolicy(抛异常):除非业务能容忍任务丢失,否则可能中断上游逻辑 - 避免
DiscardPolicy和DiscardOldestPolicy:无日志、易丢关键数据,问题难定位
加上命名线程工厂和基础监控
没有可观测性,参数调优就是盲调。
- 用
ThreadFactoryBuilder或自定义工厂,生成带业务标识的线程名,如"order-process-%d",方便日志追踪 - 手动构建
ThreadPoolExecutor后,可实时获取关键指标:getQueue().size()查积压量,getActiveCount()看活跃线程数 - 建议设置告警阈值:队列使用率持续超过 80%,或活跃线程长期占满最大值,就该介入排查
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










