线程池(二)ThreadPoolExector
线程池(二)ThreadPoolExector
程序员朱永胜有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步, 认准
https://blog.zysicyj.top
可点击链接
https://blog-1253652709.cos.ap-guangzhou.myqcloud.com//picgo/202401180921373.png
解答疑问
线程池执行器 (ThreadPoolExecutor) 详解
目录
- 简介
- ThreadPoolExecutor 构造函数
- 核心参数
- 执行任务流程
- 线程池的状态
- 源码分析
- 应用场景
- 注意事项
简介
ThreadPoolExecutor 是 Java 中用于管理线程池的核心类。它提供了一种灵活而强大的机制来创建和管理线程池,允许我们通过线程池来控制并发任务的执行。通过合理地配置和使用线程池,我们可以显著提升应用程序的性能和响应能力。
ThreadPoolExecutor 构造函数
ThreadPoolExecutor 提供了多种构造函数,最常用的是如下这个:
1 | public ThreadPoolExecutor( |
参数解析:
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 会按以下步骤进行处理:
- 如果线程池中的线程数小于 corePoolSize,即使有空闲线程,也会创建一个新线程来执行新任务。
- 如果线程池中的线程数达到或超过 corePoolSize,新任务会被添加到工作队列中。
- 如果工作队列已满,且线程数小于 maximumPoolSize,会创建新的线程来执行任务。
- 如果线程数达到 maximumPoolSize 且工作队列已满,会根据拒绝策略处理新任务。
线程池的状态
线程池在运行过程中会有以下几种状态:
- RUNNING: 接受新任务并处理队列中的任务。
- SHUTDOWN: 不接受新任务,但会处理队列中的任务。
- STOP: 不接受新任务,也不处理队列中的任务,并中断正在执行的任务。
- TIDYING: 所有任务都已终止,工作线程数为 0,线程池进入该状态并将运行
terminated()
钩子方法。 - TERMINATED:
terminated()
方法执行完成。
源码分析
接下来我们深入分析 ThreadPoolExecutor
的核心源码,了解其内部工作机制。
任务提交
任务提交的方法有 execute(Runnable command)
和 submit(Callable<T> task)
。以下是 execute
方法的部分源码:
1 | public void execute(Runnable command) { |
该方法分为三个步骤:
- 尝试创建核心线程:如果当前线程数小于
corePoolSize
,尝试创建一个新的线程来执行任务。 - 任务入队列:如果线程数已达到
corePoolSize
,将任务加入工作队列。 - 创建非核心线程:如果队列已满且线程数小于
maximumPoolSize
,创建新的线程。如果线程数已达到maximumPoolSize
,执行拒绝策略。
应用场景
ThreadPoolExecutor
在以下场景中非常有用:
- Web 服务器:处理大量并发请求,避免因频繁创建和销毁线程带来的开销。
- 后台任务:执行异步任务,如日志处理、数据备份等。
- 定时任务:结合
ScheduledThreadPoolExecutor
,可以定时或周期性地执行任务。
注意事项
- 合理配置线程池参数:过大的线程池会导致资源浪费,过小的线程池则无法充分利用系统资源。
- 选择合适的任务队列:不同类型的任务队列适用于不同的场景,需要根据实际需求选择合适的队列。
- 处理异常情况:要处理好线程池中的异常情况,避免线程意外中断导致任务丢失。
- 监控线程池状态:可以通过
ThreadPoolExecutor
提供的getPoolSize()
、getActiveCount()
、getCompletedTaskCount()
等方法监控线程池的运行状态。
通过深入理解和合理配置 ThreadPoolExecutor
,我们可以在实际项目中高效地管理并发任务,提升应用的性能和稳定性。