📚 目录
[1. 线程池是什么](#1. 线程池是什么)
[2. ThreadPoolExecutor 全参构造方法介绍](#2. ThreadPoolExecutor 全参构造方法介绍)
[3. 七大构造参数逐个详解](#3. 七大构造参数逐个详解)
[4. 完整代码演示](#4. 完整代码演示)
[5. 小结](#5. 小结)
前言:
Java多线程开发里,手动new Thread创建线程弊端极多:频繁创建销毁线程开销大、线程无管控容易无限创建打满CPU、任务无法统一管理。线程池就是用来复用线程、管控任务的核心工具,也是面试高频考点。
1. 线程池是什么
我们提前创建一批线程,放入到一个池子当中,当我们想要用其中一个线程时,我们可以直接从这个池子中取,用完这个线程时,又把线程放回到线程池中,不会销毁线程;

这个就好比我们生活中的锅碗瓢盆,当我们想要使用其中一个东西的时候,我们可以直接在厨房中进行取,此时厨房就相当于一个线程池,我们做饭的东西都放在厨房中,锅碗瓢盆等待我们使用;
总结:线程池是预先创建一批线程存放于池中,程序有异步任务时,直接复用池内空闲线程执行任务;任务执行完毕,线程不会销毁,回归池子等待复用。
好处:
- 降低资源消耗->
可以直接对比于一次性碗和陶瓷碗 - 提升相应速度->
不用重复进行创建,好比做饭直接在厨房中进行拿去即可,不需要重新去超市买 - 方便管理 ->
集中,并且日常厨房中不需要过多的锅碗瓢盆,我们可以主动限制买多少个 - 便于维护和运行->
可以统计锅碗瓢盆的使用次数,和使用
[🔙 返回目录](#🔙 返回目录)
2. ThreadPoolExecutor 全参构造方法介绍
Java中我们可以使用ThreadPoolExecutor构造器创建线程池,也可以使用Executors来创建线程池;
具体使用哪一种方法,取决于公司的业务要求,ExecutorsJava官方对ThreadPoolExecutor进行进一步封装,方便我们日常中使用;
而在阿里公司中明确规定书写线程池必须使用ThreadPoolExecutor,防止出现线程安全问题;
java
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//底层源码省略
}
[🔙 返回目录](#🔙 返回目录)
3. 七大构造参数逐个详解
corePoolSize 与 maximumPoolSize
corePoolSize :核心线程数,线程池中不会在空闲时自动回收,是线程池中时常保留的线程;
maximumPoolSize : 最大线程数,线程池中允许创建最大的线程数量,最大线程数 = 核心线程数+临时线程数
举一个简单的例子:
一家公司里面有5个正式员工,一天公司业务忙不过来了,老板有两个方案,一个是去招正式员工,一个去招实习生,如果
招正式员工,等业务空闲的时候,员工就没事干,有的就开始摸鱼了,开除的话又需要考虑赔钱,此时老板就能去招实习
业务忙的时候就多招实习生,业务不忙了就可以给实习生开了,不需要赔钱,此时一举两得;
此时:正式员工就相当于核心线程,实习生就相当于临时的
[🔙 返回目录](#🔙 返回目录)
long keepAliveTime 与TimeUnit unit
两个参数进行搭配使用,管控非核心临时线程的空闲回收规则;
keepAliveTime : 代表非核心线程等待任务的时间,当临时线程无事可做,超出等待实现时,JVA会销毁释放资源;
默认只对非核心线程生效,如果调用调用threadPool.allowCoreThreadTimeOut(true) 开启后,核心线程同样会遵守该超时规则空闲回收。
unit : 时间单位枚举类TimeUnit,用来指定 keepAliveTime 的时间单位,常使用枚举:
- TimeUnit.SECONDS:秒(日常最常用)
- TimeUnit.MILLISECONDS:毫秒
- TimeUnit.MINUTES:分钟
- TimeUnit.HOURS:小时
具体采用哪一个时间单位,根据需求进行选取;
[🔙 返回目录](#🔙 返回目录)
BlockingQueue<Runnable> workQueue
BlockingQueue<Runnable> workQueue : 阻塞队列,用于存放等待执行的runnable任务,当核心线程都处于忙碌状态时,新提交的任务不会创建新的线程,而是进入阻塞队列进行等待,当队列满之后,线程池才会创建非核心线程;
主流使用的阻塞队列:
| 队列类型 | 特点 | 生产环境注意事项 |
|---|---|---|
| ArrayBlockingQueue | 有界队列,初始化必须指定固定容量 | 推荐日常业务使用,容量可控,不会无限堆积任务 |
| LinkedBlockingQueue | 可设置容量;不传参则为无界队列 | 无界模式任务可无限存入,极易引发 OOM 内存溢出,禁止裸用 |
| SynchronousQueue | 不存储任何任务,入队操作必须有线程同时接收 | 没有排队能力,任务进来直接触发扩容线程,线程容易拉满 |
- 队列是线程池流程的第二优先级:核心线程 > 队列 > 临时线程 > 拒绝策略;
- 高并发业务优先选用ArrayBlockingQueue限制队列长度,杜绝内存风险;
- Executors工具类很多默认使用无界LinkedBlockingQueue,这是阿里规范禁止直接使用 Executors 的核心原因。
[🔙 返回目录](#🔙 返回目录)
ThreadFactory threadFactory
又名:线程工厂->专门用于自定义创建线程,线程池中所有新线程都由线程工厂产出;
默认使用Executors.defaultThreadFactory(),创建普通非守护线程、无自定义线程名。
线程工厂在生产环境中存在的价值:
-
自定义线程名称,线上排查日志、堆栈时,根据线程名快速区分业务线程,定位故障
-
可以设置守护线程,线程优先级
-
可以给线程绑定线程组,方便管理
[🔙 返回目录](#🔙 返回目录)
RejectedExecutionHandler handler
全称:拒绝策略,
触发时机: 核心线程满、阻塞队列塞满、最大线程数也打满,线程池没有任何能力处理新任务时 ,执行拒绝策略。
| 拒绝策略类 | 行为逻辑 | 使用场景 |
|---|---|---|
| AbortPolicy(默认) | 直接抛出RejectedExecutionException异常,中断任务提交 | 普通业务,希望任务失败立刻报错,方便感知异常 |
| CallerRunsPolicy | 让提交任务的调用线程执行当前被拒绝任务 | 不允许丢任务,削峰限流,最慢但保证任务必执行 |
| DiscardPolicy | 默默丢弃新提交的任务,无报错无日志 | 允许丢任务,非核心日志、埋点等不重要任务 |
| DiscardOldestPolicy | 丢掉队列最老未执行任务,尝试放入新任务 | 时效性优先,新任务价值更高(消息实时推送) |
举一个简单的例子->门店客服:正式员工(核心线程)全忙、等候坐席(队列)坐满、临时兼职(最大线程)也全部在岗。
再来新客户(新任务),拒绝策略决定怎么处理:
Abort:直接拒绝接待,告知客户爆满报错;
CallerRuns:前台接待员自己停下手里工作接待客户;
Discard:假装没看见,忽略客户;
DiscardOldest:把排队最久的客户赶走,接待新来的。
生产环境建议:
- 绝大多数业务推荐 CallerRunsPolicy,不会丢数据,天然限流;
- 定时、埋点、日志上报选 DiscardPolicy;
- 时效性强的推送业务用 DiscardOldestPolicy;
- 默认 AbortPolicy 容易线上大量抛异常,线上不推荐直接使用。
[🔙 返回目录](#🔙 返回目录)
4. 完整代码演示
java
public class Demo1 {
public static void main(String[] args) {
//定义线程工厂
ThreadFactory businessThreadFactory = new ThreadFactory () {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("thread-" + threadNumber.incrementAndGet());
return thread;
}
};
//定义拒绝策略
RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("任务被线程池拒绝,可自定义处理逻辑");
}
};
//创建7个构造方法的线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
3, // 核心线程数
8, // 最大线程数
5L, // 非核心线程空闲存活时长
TimeUnit.SECONDS, // 时间单位:秒
new ArrayBlockingQueue<>(20), // 有界阻塞队列,容量20
businessThreadFactory, // 自定义线程工厂
customHandler // 自定义拒绝策略
);
// 批量提交30个测试任务
for (int i = 1; i <= 30; i++) {
int taskId = i;
threadPool.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务:" + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 程序结束关闭线程池
threadPool.shutdown();
}
}
结果参考:

[🔙 返回目录](#🔙 返回目录)
5. 小结
构造参数:
| 参数 | 作用 |
|---|---|
| corePoolSize | 常驻核心线程,不会被超时回收 |
| maximumPoolSize | 线程总数上限 = 核心线程 + 临时非核心线程 |
| keepAliveTime | 非核心线程空闲多久没人用就销毁 |
| unit | 空闲时间单位(时分秒毫秒) |
| workQueue | 存放等待任务的阻塞队列,优先缓冲削峰 |
| threadFactory | 负责创建线程,推荐自定义命名方便排错 |
| handler | 线程池满载后的兜底拒绝方案 |
执行流程:
提交任务 -> 核心线程数量没满:核心线程执行 -> 核心线程满:任务放入阻塞队列 -> 队列存满,创建线程数达到最大线程数 -> 最大线程满,执行拒绝策略
线程池总结:
线程池通过复用线程减少频繁创建销毁线程的开销,流程是先走核心线程、再走队列、再扩容临时线程,最后触发拒绝;生产环境必须手动指定七大参数,约束队列长度、自定义线程名,根据业务选型拒绝策略,规避 OOM 和线上故障。
[🔙 返回目录](#🔙 返回目录)