线程池执行流程详解

线程池(Thread Pool)是一种基于池化思想管理线程 的并发工具,核心目标是复用线程资源、控制并发数量、降低资源开销 。其执行流程涉及任务提交、线程调度、任务排队、拒绝策略等多个环节,是Java并发编程中最常用的多线程管理方案(如ThreadPoolExecutor)。

一、线程池的核心组件

在理解执行流程前,需先明确线程池的核心组件及其作用:

组件 说明
核心线程数(corePoolSize)​ 线程池长期保留的最小线程数(即使空闲也不会被销毁,除非设置allowCoreThreadTimeOut)。
最大线程数(maximumPoolSize)​ 线程池允许创建的最大线程数(核心线程+非核心线程的总和)。
任务队列(workQueue)​ 当核心线程全忙时,新任务暂时存入的队列(如ArrayBlockingQueueLinkedBlockingQueue)。
非核心线程存活时间(keepAliveTime)​ 非核心线程(超出核心线程数的线程)空闲多久后被回收的时间阈值。
拒绝策略(RejectedExecutionHandler)​ 当任务无法被核心线程、队列、非核心线程处理时,执行的回调策略(如丢弃任务、抛异常)。
线程工厂(ThreadFactory)​ 用于创建线程的工厂类(可自定义线程名称、优先级等)。

二、线程池执行流程:从任务提交到执行

线程池的执行流程可分为5个阶段 ,核心逻辑是"优先复用核心线程→队列缓冲→创建非核心线程→触发拒绝策略"。以下是详细步骤:

阶段1:提交任务(submit/execute)​

用户通过execute(Runnable task)submit(Callable task)提交任务到线程池。线程池接收到任务后,立即进入调度逻辑。

阶段2:核心线程是否空闲?​

线程池首先检查核心线程是否都在忙碌​:

  • 若有空闲的核心线程:直接由空闲核心线程执行任务(无需入队)。
  • 若核心线程全忙:进入阶段3(任务入队)。

阶段3:任务入队(workQueue.offer())​

当核心线程全忙时,线程池会将新任务尝试存入任务队列​:

  • 队列未满:任务成功入队,等待核心线程空闲后从队列头部取出执行。
  • 队列已满:进入阶段4(创建非核心线程)。

阶段4:创建非核心线程(workerCount < maximumPoolSize)​

若任务队列已满,线程池会检查当前线程总数(核心线程+非核心线程)是否小于maximumPoolSize

  • :创建非核心线程(临时线程)执行该任务(非核心线程空闲超时后会被回收)。
  • :进入阶段5(触发拒绝策略)。

阶段5:触发拒绝策略(RejectedExecutionHandler)​

当以下3个条件同时满足时,触发拒绝策略:

  1. 核心线程全忙;
  2. 任务队列已满;
  3. 线程总数已达maximumPoolSize(无法创建新线程)。

此时线程池会调用RejectedExecutionHandler处理任务,常见策略有4种:

策略 行为
AbortPolicy(默认)​ 直接抛出RejectedExecutionException异常,拒绝任务。
CallerRunsPolicy 由调用者(提交任务的线程)直接执行该任务("自己干")。
DiscardPolicy 静默丢弃任务,不抛异常也不执行。
DiscardOldestPolicy 丢弃队列中最旧的任务(队头任务),然后将新任务重新入队(尝试再次调度)。

关键补充:核心线程的超时回收

若设置了allowCoreThreadTimeOut(true),核心线程在空闲超时(keepAliveTime)后也会被回收,此时线程池可能降级到0线程(仅保留任务队列中的任务)。

三、线程池架构图(ASCII版)

以下是线程池执行流程的架构图,展示了组件间的关系和任务流动路径:

markdown 复制代码
+-------------------+          +------------------+          +------------------+          +------------------+  
|  提交任务          | -------> |  核心线程池       | -------> |  任务队列        | -------> |  非核心线程池     |  
| (execute/submit)  |          | (corePoolSize)   |          | (workQueue)      |          | (maxPoolSize-core)|  
+-------------------+          +------------------+          +------------------+          +------------------+  
         ↑                             ↓                              ↓                              ↓  
         |(核心线程空闲)             |(核心线程忙,队列未满)         |(队列满,线程数未达上限)      |(线程数达上限)              |  
         |                             |                              |                              |  
         |                             v                              v                              v  
         |                  +------------------+          +------------------+          +------------------+  
         |                  |  核心线程执行任务  |          |  任务入队等待     |          |  创建非核心线程   |  
         |                  | (Worker.run())   |          | (Queue.offer())  |          | (new Worker())    |  
         |                  +------------------+          +------------------+          +------------------+  
         |                                                                                           |  
         |                                                                                           v  
         |                                                                                  +------------------+  
         |                                                                                  |  触发拒绝策略      |  
         |                                                                                  | (RejectedHandler) |  
         |                                                                                  +------------------+  
         ↓                                                                                           |  
+-------------------+                                                                          +------------------+  
|  任务完成/异常      | <----------------------------------------------------------------------- |  任务执行/丢弃     |  
| (Future.get())    |                                                                          +------------------+  
+-------------------+  


### 图解说明:  
1. **任务提交**:用户通过`execute`提交任务,线程池开始调度。  
2. **核心线程执行**:若有空闲核心线程,直接执行任务(`Worker`线程通过`run()`方法处理任务)。  
3. **任务入队**:核心线程忙时,任务入队(如`ArrayBlockingQueue`),等待核心线程空闲后消费。  
4. **非核心线程创建**:队列满且线程数未达上限时,创建非核心线程执行任务。  
5. **拒绝策略**:队列满且线程数达上限时,触发拒绝策略处理任务。  


## 四、执行流程示例(以`ThreadPoolExecutor`为例)  
假设线程池配置:`corePoolSize=2`,`maximumPoolSize=5`,`workQueue=ArrayBlockingQueue(3)`,`rejectedPolicy=AbortPolicy`。  

1. **提交前2个任务**:核心线程(2个)空闲,直接执行。  
2. **提交第3-5个任务**:核心线程忙,任务依次入队(队列容量3,此时队列满)。  
3. **提交第6个任务**:核心线程忙+队列满,创建非核心线程(第3个线程)执行。  
4. **提交第7-9个任务**:继续创建非核心线程(第4、5个线程)执行。  
5. **提交第10个任务**:核心线程(2)+非核心线程(3)=5(达`maximumPoolSize`),队列已满(3),触发`AbortPolicy`,抛出异常。  


## 五、总结  
线程池的执行流程本质是**"资源分级利用"**:  
- 核心线程:长期驻留,减少线程创建开销;  
- 任务队列:缓冲突发流量,避免频繁创建线程;  
- 非核心线程:应对短期流量高峰,空闲后回收;  
- 拒绝策略:防止资源耗尽,保护系统稳定性。  

理解这一流程,能帮助开发者合理配置线程池参数(如`corePoolSize`、`workQueue`类型),避免"线程爆炸"或"任务堆积"问题,是高并发系统设计的必备技能。
相关推荐
uhakadotcom4 小时前
在chrome浏览器插件之中,options.html和options.js常用来做什么事情
前端·javascript·面试
程序员清风5 小时前
Dubbo RPCContext存储一些通用数据,这个用手动清除吗?
java·后端·面试
南北是北北6 小时前
JetPack ViewBinding
面试
南北是北北6 小时前
jetpack ViewModel
面试
渣哥7 小时前
Lazy能否有效解决循环依赖?答案比你想的复杂
javascript·后端·面试
前端架构师-老李8 小时前
面试问题—你接受加班吗?
面试·职场和发展
ANYOLY8 小时前
多线程&并发篇面试题
java·面试
南北是北北8 小时前
RecyclerView 的数据驱动更新
面试
uhakadotcom8 小时前
coze的AsyncTokenAuth和coze的TokenAuth有哪些使用的差异?
后端·面试·github