有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准
https://blog.zysicyj.top
可点击链接
https://blog-1253652709.cos.ap-guangzhou.myqcloud.com//picgo/202401180921373.png解答疑问
线程池执行器 (ThreadPoolExecutor) 详解
目录
- 简介
- ThreadPoolExecutor 构造函数
- 核心参数
- 执行任务流程
- 线程池的状态
- 源码分析
- 应用场景
- 注意事项
简介
ThreadPoolExecutor 是 Java 中用于管理线程池的核心类。它提供了一种灵活而强大的机制来创建和管理线程池,允许我们通过线程池来控制并发任务的执行。通过合理地配置和使用线程池,我们可以显著提升应用程序的性能和响应能力。
ThreadPoolExecutor 构造函数
ThreadPoolExecutor 提供了多种构造函数,最常用的是如下这个:
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数解析:
- corePoolSize: 核心线程数,线程池中始终保持活动的线程数。
- maximumPoolSize: 最大线程数,线程池中允许的最大线程数。
- keepAliveTime: 线程空闲时间,当线程数大于 corePoolSize 时,多余的空闲线程存活的时间。
- unit: 时间单位,用于指定 keepAliveTime 的时间单位。
- workQueue: 任务队列,用于存放待执行的任务。
- threadFactory: 线程工厂,用于创建新线程。
- handler: 拒绝策略,当任务无法提交到线程池时的处理方式。
核心参数
corePoolSize 和 maximumPoolSize:
- 核心线程数 (corePoolSize) 是线程池中保持活跃的线程数,即使这些线程处于空闲状态也不会被销毁。
- 最大线程数 (maximumPoolSize) 是线程池中允许的最大线程数,当队列中的任务满了且当前线程数小于
maximumPoolSize时,线程池会创建新的线程来执行任务。
keepAliveTime 和 unit:
- 空闲时间 (keepAliveTime) 是当线程数超过
corePoolSize时,多余的空闲线程的存活时间,超过这个时间,空闲线程会被销毁。 - 时间单位 (unit) 是一个
TimeUnit枚举,用于指定keepAliveTime的时间单位。
- 空闲时间 (keepAliveTime) 是当线程数超过
workQueue:
- 任务队列 (workQueue) 是一个阻塞队列,用于存放待执行的任务。常用的阻塞队列包括
ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue等。
- 任务队列 (workQueue) 是一个阻塞队列,用于存放待执行的任务。常用的阻塞队列包括
threadFactory:
- 线程工厂 (threadFactory) 用于创建线程,一般可以通过自定义
ThreadFactory来设置线程的名称、优先级等属性。
- 线程工厂 (threadFactory) 用于创建线程,一般可以通过自定义
handler:
- 拒绝策略 (RejectedExecutionHandler) 当线程池和队列都满了,再有任务提交时的处理策略。Java 提供了四种默认的拒绝策略:
AbortPolicy: 抛出RejectedExecutionException。CallerRunsPolicy: 由调用线程处理该任务。DiscardPolicy: 丢弃任务,不予处理。DiscardOldestPolicy: 丢弃队列中最旧的任务,然后尝试提交新任务。
- 拒绝策略 (RejectedExecutionHandler) 当线程池和队列都满了,再有任务提交时的处理策略。Java 提供了四种默认的拒绝策略:
执行任务流程
当一个任务提交到线程池时,ThreadPoolExecutor 会按以下步骤进行处理: 1. 如果线程池中的线程数小于 corePoolSize,即使有空闲线程,也会创建一个新线程来执行新任务。 2. 如果线程池中的线程数达到或超过 corePoolSize,新任务会被添加到工作队列中。 3. 如果工作队列已满,且线程数小于 maximumPoolSize,会创建新的线程来执行任务。 4. 如果线程数达到 maximumPoolSize 且工作队列已满,会根据拒绝策略处理新任务。
线程池的状态
线程池在运行过程中会有以下几种状态:
- RUNNING: 接受新任务并处理队列中的任务。
- SHUTDOWN: 不接受新任务,但会处理队列中的任务。
- STOP: 不接受新任务,也不处理队列中的任务,并中断正在执行的任务。
- TIDYING: 所有任务都已终止,工作线程数为 0,线程池进入该状态并将运行 terminated() 钩子方法。
- TERMINATED: terminated() 方法执行完成。
源码分析
接下来我们深入分析 ThreadPoolExecutor 的核心源码,了解其内部工作机制。
任务提交
任务提交的方法有 execute(Runnable command) 和 submit(Callable<T> task)。以下是 execute 方法的部分源码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
该方法分为三个步骤:
1. 尝试创建核心线程:如果当前线程数小于 corePoolSize,尝试创建一个新的线程来执行任务。
2. 任务入队列:如果线程数已达到 corePoolSize,将任务加入工作队列。
3. 创建非核心线程:如果队列已满且线程数小于 maximumPoolSize,创建新的线程。如果线程数已达到 maximumPoolSize,执行拒绝策略。
应用场景
ThreadPoolExecutor 在以下场景中非常有用:
- Web 服务器:处理大量并发请求,避免因频繁创建和销毁线程带来的开销。
- 后台任务:执行异步任务,如日志处理、数据备份等。
- 定时任务:结合 ScheduledThreadPoolExecutor,可以定时或周期性地执行任务。
注意事项
- 合理配置线程池参数:过大的线程池会导致资源浪费,过小的线程池则无法充分利用系统资源。
- 选择合适的任务队列:不同类型的任务队列适用于不同的场景,需要根据实际需求选择合适的队列。
- 处理异常情况:要处理好线程池中的异常情况,避免线程意外中断导致任务丢失。
- 监控线程池状态:可以通过
ThreadPoolExecutor提供的getPoolSize()、getActiveCount()、getCompletedTaskCount()等方法监控线程池的运行状态。
通过深入理解和合理配置 ThreadPoolExecutor,我们可以在实际项目中高效地管理并发任务,提升应用的性能和稳定性。


