api-java.util.concurrent.ThreadPoolExecutor

I、UML api-java.util.concurrent.ThreadPoolExecutor
文章图片

II、依赖 api-java.util.concurrent.ThreadPoolExecutor
文章图片

III、运行流程 api-java.util.concurrent.ThreadPoolExecutor
文章图片

IV、生命周期 【api-java.util.concurrent.ThreadPoolExecutor】api-java.util.concurrent.ThreadPoolExecutor
文章图片

run state desc
RUNNING 能接收新任务,且能处理队列中任务
SHUTDOWN 不能接收新任务,但能继续处理队列中任务
STOP 新任务和队列中任务都不能处理,会中断正在处理任务的线程
TIDYING 所有任务已终止
TERMINATED terminated()之后的状态
api-java.util.concurrent.ThreadPoolExecutor
文章图片

V、任务调度 1、首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
2、如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
3、如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
4、如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
5、如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
api-java.util.concurrent.ThreadPoolExecutor
文章图片

VI、Worker api-java.util.concurrent.ThreadPoolExecutor
文章图片

1、任务是 Runnable(内部变量名叫 task 或 command),线程是 Worker。
2、Worker持有一个线程thread
3、Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。
——lock方法一旦获取了独占锁,表示当前线程正在执行任务中。
——如果正在执行任务,则不应该中断线程。
——如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。
——线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。
4、执行任务流程
api-java.util.concurrent.ThreadPoolExecutor
文章图片

VII、需要关注和验证的几个重要知识点
1、线程池初始化线程数是可动态调整的。调小时任务将重新进入for循环。验证调大和调小时任务是否有影响。 2、线程池一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。创建销毁线程的开销、调度线程的开销。怎么验证这两种开销??? 3、设置的线程数过多可能还会引发线程上下文切换频繁的问题,也会降低处理任务的速度,降低吞吐量???还是怎么验证开销??? 4、最大核心数设置偏小,大量抛出RejectedExecutionException,触发接口降级条件。就是说是一种动态降级的方案。 5、队列设置过长,最大线程数设置失效,导致请求数量增加时,大量任务堆积在队列中,任务执行时间过长。嵌套的线程池,会不会出现第一层的任务正在执行,与之关联的第二层任务正在等待,从而导致响应时间长???

VIII、参考文章:
https://www.javadoop.com/post/java-thread-pool https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

    推荐阅读