Java基础回顾:ThreadPoolExecutor
个人笔记,网上资料加源码整理,侵删
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222609633-2001258237.png)
1. 主要参数
int corePoolSize
核心线程数
int maximumPoolSize
最大线程数
long keepAliveTime
线程等待时间
TimeUnit unit
时间单位
BlockingQueue<Runnable> workQueue
阻塞队列
ThreadFactory threadFactory
线程工厂,用来创建线程
RejectedExecutionHandler handler
拒绝策略
2. 执行流程
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222649722-223112160.png)
3. 运行机制
1)线程池如何维护自身状态
线程池内部维护两个值:运行状态(runState)和线程数量(workerCount),用一个AtomicInteger,高三位保存运行状态,低29位保存线程数量。
内部封装的获取生命周期状态和线程池数量的方法:
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
ThreadPoolExecutor运行状态分为五种:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222738535-1614114551.png)
生命周期转换如下:
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222748198-205553209.png)
2)线程池如何管理任务
线程池运行核心机制
所有任务的调度都是有execute方法完成的,这部分完成的工作是:检查当前线程池的运行状态、工作线程数、运行策略,决定接下来的执行流程,是直接申请线程执行,或是缓冲到队列中等待执行,亦或是直接拒绝执行该任务。
执行过程如下:
- 检查线程池运行状态,如果不是RUNNING状态,则直接拒绝执行任务
- 如果workerCount < corePoolSize,则直接创建一个线程并执行任务
- 如果workerCount >= corePoolSize,但是阻塞队列没满,将任务放入阻塞队列等待执行
- 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且阻塞队列已满,创建一个新线程执行任务
- 如果workerCount > maximumPoolSize,并且阻塞队列已满,则根据拒绝策略来处理该任务,默认处理方式是直接抛出异常
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222812724-681561535.png)
拒绝策略
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222825859-1172751766.png)
3)线程池如何管理线程
线程池为了掌握线程的状态并维护线程的生命周期,设计了线程池内的工作线程Worker
Worker线程执行任务模型:
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222840117-1272257014.png)
Worker通过继承AQS(AbstractQueuedSynchronizer),使用AQS来实现独占锁这个功能
AQS使用一个int成员变量state来表示同步状态,如ReentrantLock的lock方法,实际上就是通过compareAndSetState(0, 1)方法使state值0加到1,维护state值来获取锁和释放锁。通过内置的FIFO队列来完成获取资源线程的排队工作(公平锁)。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222911732-517889534.png)
线程池中线程的销毁依赖JVM自动回收,线程池做的工作是根据当前线程池的状态维护一定数量的线程引用,防止这部分线程被JVM回收。
Worker被创建出来后,就会不断的进行轮询,然后获取任务去执行,核心线程可以无限等待获取任务,非核心线程要限时获取任务。当Worker无法获取到任务,也就是获取的任务为空时,循环会结束,Worker会主动消除自身在线程池内的应用
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222934535-1658276958.png)
Worker线程执行任务流程
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013222948124-1162697192.png)
4)线程池锁机制
AQS,lock,unlock
非重入锁
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013223013540-250239273.png)
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
可重入锁(ReentrantLock)
- 公平锁 FIFO,先到先得原则
- 非公平锁
重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞
public int getPoolSize() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Remove rare and surprising possibility of
// isTerminated() && getPoolSize() > 0
return runStateAtLeast(ctl.get(), TIDYING) ? 0
: workers.size();
} finally {
mainLock.unlock();
}
}
4. 主要业务场景
后台数据导出任务
快速响应用户请求
快速处理批量任务
一点浩然气,千里快哉风!