必须用线程池代替手动创建线程,因其能复用线程、控制并发、避免oom和失控;推荐手写threadpoolexecutor,配准corepoolsize、maxpoolsize、有界队列及拒绝策略,并规范命名、关闭与异常处理。

直接用线程池代替每次 new Thread().start(),能显著降低系统开销——核心在于复用线程、控制并发、避免资源失控。
为什么必须用线程池而不是手动创建线程
手动创建线程在高并发下会快速暴露三大硬伤:
- 每次 new Thread 都要分配栈内存(默认1MB)、初始化上下文,CPU和内存开销大
- 无上限创建线程容易触发 OOM,比如 1000 个并发请求可能瞬间拉起上千线程
- 线程散落各处,无法统一监控、无法设置超时、无法做拒绝处理,出问题难定位
线程池把“创建-执行-销毁”变成“预热-复用-回收”,相当于把一次性筷子换成可循环餐具。
选对线程池类型是第一步
Executors 提供的工厂方法看似方便,但多数有隐患,生产环境建议优先手写 ThreadPoolExecutor:
- newFixedThreadPool(n):底层用无界 LinkedBlockingQueue,任务积压会吃光内存,不推荐
- newCachedThreadPool():maximumPoolSize = Integer.MAX_VALUE,突发流量可能创建海量线程,拖垮系统
- newSingleThreadExecutor():适合串行化任务(如日志刷盘),但吞吐低
- 推荐做法:用 ThreadPoolExecutor 显式指定 corePoolSize、maxPoolSize、有界队列、拒绝策略
关键参数要按场景配准
四个参数决定线程池是否“听话”:
- corePoolSize:常驻线程数,设为 CPU 核心数 × (1 + 平均等待时间 / 平均执行时间),IO 密集型可适当调高
- maximumPoolSize:最大线程数,建议不超过 corePoolSize 的 2–3 倍,防止上下文切换风暴
- workQueue:必须用有界队列,如 new ArrayBlockingQueue(200),避免任务无限堆积
- RejectedExecutionHandler:别用默认的 AbortPolicy(直接抛异常),建议用 CallerRunsPolicy(让提交线程自己执行)或自定义日志+告警策略
使用中必须注意的实操细节
光配对参数还不够,日常使用要守住几条底线:
- 提交任务统一用 submit()(返回 Future)或 execute()(无返回),别混用
- 线程池对象建议声明为 static final,避免重复创建
- 业务线程池必须命名:通过 ThreadFactory 设置线程名(如 “order-pool-%d”),方便排查日志
- 应用关闭前调用 shutdown() + awaitTermination(),确保任务收尾
- 避免在任务中捕获并吞掉 RuntimeException,否则线程可能静默失效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











