Java 线程池详解
Java 线程池(Thread Pool)详解
在 Java 中,线程池(Thread Pool) 是用于管理和复用线程的资源池。
相比于手动 new Thread(),线程池具有以下优势:
- 显著减少线程创建 / 销毁的性能开销
- 有效控制系统中的 In-flight(在途)任务数量
- 防止线程无限增长导致系统失控
Java 通过 java.util.concurrent(JUC) 包中的
ThreadPoolExecutor 提供最核心、最底层的线程池实现。
⚠️ 阿里巴巴 Java 开发手册明确规定:必须显式使用
ThreadPoolExecutor,禁止直接使用Executors工具类。
一、ThreadPoolExecutor 构造方法(七大核心参数)
理解线程池,本质就是理解这 7 个参数,它们共同决定了线程池的“性格”。
public ThreadPoolExecutor(
int corePoolSize, // 1. 核心线程数
int maximumPoolSize, // 2. 最大线程数
long keepAliveTime, // 3. 非核心线程空闲存活时间
TimeUnit unit, // 4. 时间单位
BlockingQueue<Runnable> workQueue, // 5. 任务阻塞队列
ThreadFactory threadFactory, // 6. 线程工厂(建议自定义,便于定位问题)
RejectedExecutionHandler handler // 7. 拒绝策略
)
二、任务处理流程(重点)
当一个新任务通过 execute() 提交时,线程池严格遵循以下 “三阶一拒绝” 逻辑:
1 核心期(Core Phase)
当前线程数 < corePoolSize
即使有空闲线程,也会创建新线程执行任务
2 排队期(Queue Phase)
核心线程已满
任务被放入 workQueue 阻塞队列中等待
3 爆发期(Max Phase)
队列已满
当前线程数 < maximumPoolSize
创建 非核心线程 紧急处理任务
4 拒绝期(Reject Phase)
线程数已达 maximumPoolSize
队列也已满
触发 拒绝策略(RejectedExecutionHandler)
三 阻塞队列 (workQueue)
队列决定了线程池如何“缓冲”任务:
- ArrayBlockingQueue:有界队列,防止 OOM(内存溢出)。
- LinkedBlockingQueue:常用,默认是无界的(小心 OOM),也可指定容量。
- SynchronousQueue:不存储任务的队列。每个插入操作必须等待一个获取操作,适合高吞吐量的直接移交(如
CachedThreadPool)。
如何确定 corePoolSize?通常根据任务性质分为两类:
- CPU 密集型(计算、加密、压缩):
- 核心线程数 = CPU 核数 + 1。
- 目的:尽量减少线程切换带来的损耗。
- IO 密集型(数据库操作、网络调用、文件读写):
- 核心线程数 = CPU 核数 * 2 或更高。
- 公式参考:
CPU 核数 * (1 + 线程平均等待时间 / 平均工作时间)。 - 目的:IO 阻塞时,让其他线程利用 CPU。
四 如何配置参数及线程池状态监控
1.如何确定 corePoolSize?通常根据任务性质分为两类:
-
CPU 密集型(计算、加密、压缩):
- 核心线程数 = CPU 核数 + 1。
- 目的:尽量减少线程切换带来的损耗。
-
IO 密集型(数据库操作、网络调用、文件读写):
-
核心线程数 = CPU 核数 * 2 或更高。
-
公式参考:
CPU 核数 * (1 + 线程平均等待时间 / 平均工作时间)。 -
目的:IO 阻塞时,让其他线程利用 CPU。
-
2.线程池状态监控
你可以通过以下方法实时监控线程池状态(对应你之前提到的 In-flight 概念):
getPoolSize():当前活跃线程总数。getActiveCount():正在执行任务的线程数(真正的 In-flight)。getQueue().size():在队列中排队的任务数。getCompletedTaskCount():已完成的任务总数
思考:三阶一拒绝中如何让线程创建变成 核心线程》空闲线程》任务队列
方案:重写队列的 offer 方法
这是 Tomcat 和 Dubbo 采用的经典做法。核心思路是:在调用队列的 offer 方法时,欺骗线程池说“我已满了”,迫使线程池去创建非核心线程。
- 自定义一个队列继承
LinkedBlockingQueue。 - 将
ThreadPoolExecutor的引用注入到队列中。 - 重写
offer方法:如果当前线程数 < 最大线程数,返回false(模拟队列已满)。
public class PriorityQueue extends LinkedBlockingQueue<Runnable> {
private ThreadPoolExecutor executor;
public void setExecutor(ThreadPoolExecutor executor) {
this.executor = executor;
}
@Override
public boolean offer(Runnable r) {
int currentThreads = executor.getPoolSize();
// 如果还有空间创建非核心线程,直接返回 false,迫使线程池创建线程
if (currentThreads < executor.getMaximumPoolSize()) {
return false;
}
// 如果已经达到最大线程数,才真正入队
return super.offer(r);
}
}
关键点: 这种方案下,你需要配合一个拒绝策略。因为当 offer 返回 false 且线程数达到 max 时,线程池会调用 RejectedExecutionHandler。在拒绝策略里,你需要尝试再次将任务塞进队列。

浙公网安备 33010602011771号