Java-多线程并发之线程池
ThreadPoolExecutor:
ctl:Integer原子变量,高3位表示线程池的状态,其他的位数用来记录线程池线程个数。
线程池状态:
RUNNING:接受新任务并处理阻塞队列里的任务
SHUTDOWN:拒绝新任务并处理阻塞队列的任务
STOP:拒绝新任务并抛弃阻塞队列里的任务,同时会中断正在处理的任务
TIDYING:所有任务都执行完(包括阻塞队列)后当前线程池活动线程数位0,将要调用terminated方法。
TERMINATED:终止状态。terminated方法调用完成以后的状态。
RUNNING>>SHUTDOWN:显示或隐式(finalize)调用了shutdown方法。
RUNNING/SHUTDOWN>>>>STOP:显示调用了shutdownNow方法。
SHUTDOWN>>>>>TIDYING:当线程池和任务队列都空
STOP>>>TIDYING:线程池空的时候
TIDYING>>>>TERMINATED:当terminated()hook方法执行完成时。
线程池的启动:
首先,构造线程池的时候,需要一些参数。一些重要的参数解释在 java内置的线程池笔记 文章中的结尾已经说明了一下重要参数的意义。
线程池构造完毕之后,如果用户调用了execute或者submit方法的时候,最后都会使用execute方法执行。
execute方法内部分3种情况处理任务:
- 如果当前正在执行的Worker数量比corePoolSize(基本大小)要小。直接创建一个新的Worker执行任务,会调用addWorker方法
- 如果当前正在执行的Worker数量大于等于corePoolSize(基本大小)。将任务放到阻塞队列里,如果阻塞队列没满并且状态是RUNNING的话,直接丢到阻塞队列,否则执行第3步
- 丢到阻塞失败的话,会调用addWorker方法尝试起一个新的Worker去阻塞队列拿任务并执行任务,如果这个新的Worker创建失败,调用reject方法
线程池中的这个基本大小指的是Worker的数量。一个Worker是一个Runnable的实现类,会被当做一个线程进行启动。Worker内部带有一个Runnable属性firstTask,这个firstTask可以为null,为null的话Worker会去阻塞队列拿任务执行,否则会先执行这个任务,执行完毕之后再去阻塞队列继续拿任务执行。
所以说如果Worker数量超过了基本大小,那么任务都会在阻塞队列里,当Worker执行完了它的第一个任务之后,就会去阻塞队列里拿其他任务继续执行。
Worker在执行的时候会根据一些参数进行调节,比如Worker数量超过了线程池基本大小或者超时时间到了等因素,这个时候Worker会被线程池回收,线程池会尽量保持内部的Worker数量不超过基本大小。
另外Worker执行任务的时候调用的是Runnable的run方法,而不是start方法,调用了start方法就相当于另外再起一个线程了。
Worker在回收的时候会尝试终止线程池。尝试关闭线程池的时候,会检查是否还有Worker在工作,检查线程池的状态,没问题的话会将状态过度到TIDYING状态,之后调用terminated方法,terminated方法调用完成之后将线程池状态更新到TERMINATED。
线程池的关闭:
shutdown:
shutdown方法,关闭线程池,关闭之后阻塞队列里的任务不受影响,会继续被Worker处理,但是新的任务不会被接受。回收闲置worker
shutdown方法中有一个interruptIdleWorkers()方法,用来中断闲置的worker,打断闲置Worker之后,getTask方法会返回null,然后Worker会被回收。 遍历worker,获取锁,获取到锁的很可能是闲置的worker,然后进行打断即可。
闲置Worker是这样解释的:Worker运行的时候会去阻塞队列拿数据(getTask方法),拿的时候如果没有设置超时时间,那么会一直阻塞等待阻塞队列进数据,这样的Worker就被称为闲置Worker。由于Worker也是一个AQS,在runWorker方法里会有一对lock和unlock操作,这对lock操作是为了确保Worker不是一个闲置Worker。
所以Worker被设计成一个AQS是为了根据Worker的锁来判断是否是闲置线程,是否可以被强制中断。
shutdownNow:
把状态设置成STOP。不会处理阻塞队列的任务。
打断用的是interruptWorkers。强行中断所有的worker执行,不管是不是闲置worker。回收所有的worker
关于TERMINATION:
Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态
参考文章:https://fangjian0423.github.io/2016/03/22/java-threadpool-analysis/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
2017-04-18 线程通信