线程池本质是线程复用机制,通过提前创建并重复利用线程避免频繁创建销毁开销;其核心在于ThreadPoolExecutor的7个参数协同控制调度逻辑,而非简单容器。

线程池本质是线程复用机制,不是“池子”本身
Java里的线程池不是某种特殊容器,而是对 Thread 生命周期的主动管控策略:提前创建一批线程,让它们反复执行不同任务,避免每次 new Thread().start() 带来的内核态切换、栈内存分配和GC压力。它的核心价值不在“池”这个名词,而在“复用”这个动作——就像数据库连接池复用连接,HTTP连接池复用 socket,本质都是对抗高频创建销毁的开销。
ThreadPoolExecutor 的 7 个参数决定行为,不是配置越多越稳
直接用 Executors 工厂方法(如 newFixedThreadPool(5))看似简单,但隐藏了关键控制权。真正可控的入口是 ThreadPoolExecutor 构造函数,它有 7 个参数,其中 5 个直接影响调度逻辑:
-
corePoolSize:不是“最小线程数”,而是“长期驻留线程数”——即使空闲也不会被回收(除非设了allowCoreThreadTimeOut(true)) -
maximumPoolSize:只有当workQueue满了才会触发扩容;若用无界队列(如LinkedBlockingQueue),它永远不生效 -
workQueue:选错队列等于埋雷——SynchronousQueue不存任务,逼着线程池立刻扩容;ArrayBlockingQueue有界,配合拒绝策略才可控;LinkedBlockingQueue默认无界,任务堆积会 OOM -
keepAliveTime:只约束非核心线程;核心线程默认永生,这点常被误读 -
handler:默认AbortPolicy抛异常,线上出问题时可能直接崩掉调用方;CallerRunsPolicy虽能降速,但会让业务线程卡住,需谨慎
任务提交流程不是“先排队再扩容”,而是有严格优先级
很多人以为线程池会“先塞满队列,再开新线程”,其实真实流程是带短路判断的:
- 提交任务 → 若当前线程数 corePoolSize,**立刻新建核心线程执行**(哪怕已有空闲线程!)
- 否则 → 尝试进
workQueue.offer();失败(如队列满)→ 才判断是否 maximumPoolSize,是则建非核心线程 - 若队列 offer 成功,但之后线程池被 shutdown,或刚入队就发现已非运行态,任务会被从队列中移除并触发拒绝策略
这意味着:用有界队列 + 合理 corePoolSize,才能真正控住并发峰值;靠“等队列满了再扩容”来限流,往往晚了一步。
立即学习“Java免费学习笔记(深入)”;
别忽略线程工厂和监控,它们才是生产环境的命门
默认 Executors.defaultThreadFactory() 创建的线程名全是 pool-1-thread-1 这种,线上出问题根本分不清是哪个模块的线程在狂打 CPU 或阻塞。必须自定义 ThreadFactory:
new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "biz-upload-pool-" + threadNumber.getAndIncrement());
t.setDaemon(false); // 关键:非守护线程,避免JVM提前退出
return t;
}
}
同时,别只盯着 execute(),要定期查状态:getActiveCount() 看实时负载,getQueue().size() 看积压,getCompletedTaskCount() 看吞吐。这些值不采集,等于闭眼开车。
corePoolSize 和 workQueue 的组合决定了它是“稳态控流”还是“瞬时雪崩”,而拒绝策略和线程命名这种细节,往往在凌晨三点告警里才显出真价值。










