Java线程池核心是复用线程而非频繁创建销毁,应显式使用ThreadPoolExecutor配置参数:合理设置corePoolSize和maximumPoolSize、选用有界队列、自定义ThreadFactory,并注意空闲线程管理、队列与拒绝策略选择及ThreadLocal清理。

Java 中使用线程池避免频繁创建销毁线程开销,核心是让同一线程反复执行多个任务,而不是为每个任务新建线程。关键不在于“怎么写代码”,而在于“怎么配、怎么用、怎么管”。
用 ThreadPoolExecutor 显式构造,别依赖 Executors 工厂方法
Executors 提供的 newFixedThreadPool、newCachedThreadPool 等方法看似简单,但隐藏风险:比如 newFixedThreadPool 使用无界 LinkedBlockingQueue,任务积压会导致 OOM;newCachedThreadPool 允许无限创建非核心线程,可能耗尽系统资源。推荐直接使用 ThreadPoolExecutor 构造器,明确控制每个参数:
- 设置合理的 corePoolSize(如 CPU 核心数 + 1),保证常用任务有稳定线程可用
- 设定 maximumPoolSize 上限(例如 corePoolSize × 2 或根据 I/O 密集型适当调高),防止突发流量打垮系统
- 选用有界队列(如 ArrayBlockingQueue)而非无界队列,强制触发拒绝策略,暴露真实瓶颈
- 指定自定义 ThreadFactory,给线程命名便于排查(如 “biz-task-pool-%d”)
让线程真正复用起来:理解空闲线程的生命周期
线程复用不是自动发生的,它依赖于线程池对空闲线程的保留与回收逻辑:
- 核心线程默认永驻:只要线程池处于 RUNNING 状态,corePoolSize 内的线程即使空闲也不会销毁
- 非核心线程可回收:超过 corePoolSize 的线程,在 keepAliveTime 内没拿到新任务就会被终止
- 若想让核心线程也支持超时回收,调用 allowCoreThreadTimeOut(true)(适合任务波动大的场景)
- Worker 线程内部是一个 while 循环:
while (task != null || (task = getTask()) != null),持续从队列取任务执行
配合业务特征选对队列和拒绝策略
队列类型决定任务是“排队等”还是“立刻扩线程”,直接影响复用效果:
立即学习“Java免费学习笔记(深入)”;
- SynchronousQueue:不存任务,提交即尝试找空闲线程;没空闲则触发扩容(适合任务短、吞吐高、希望快速响应的场景)
- ArrayBlockingQueue:有界队列,任务先排队;队列满才扩容;能防内存溢出,也促使你正视处理能力瓶颈
- 拒绝策略不能用默认的 AbortPolicy(直接抛异常),建议用 CallerRunsPolicy(由提交线程自己执行),或自定义日志+降级逻辑,避免丢任务
注意线程上下文清理,防止隐性泄漏
复用线程意味着 ThreadLocal 变量不会随线程销毁而清空,若任务中用了 ThreadLocal 存用户上下文、数据库连接等,不手动 remove 就会累积泄漏:
- 在 Runnable/Callable 执行前后显式调用 threadLocal.remove()
- 或使用 try-finally / try-with-resources 包裹清理逻辑
- 避免在线程池中长期持有大对象引用(如缓存、连接池句柄),除非明确设计为共享资源


















