最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了 。
不过,我倒是对线程池是如何回收工作线程比较感兴趣,所以简单分析了一下,加深对线程池的理解吧 。
那么,就以JDK1.8为例分析吧 。
1. runWorker(Worker w)工作线程启动后,就进入runWorker(Worker w)方法 。
里面是一个while循环,循环判断任务是否为空,若不为空,执行任务;若取不到任务,或发生异常,退出循环,执行processWorkerExit(w, completedAbruptly); 在这个方法里把工作线程移除掉 。
取任务的来源有两个,一个是firstTask,这个是工作线程第一次跑的时候执行的任务,最多只能执行一次,后面得从getTask()方法里取任务 。看来,getTask()是关键,在不考虑异常的场景下,返回null,就表示退出循环,结束线程 。下一步,就得看看,什么情况下getTask()会返回null 。
(篇幅有限,分段截取,省略中间执行任务的步骤)
文章插图
文章插图
2. getTask() 返回null一共有两种情况会返回null,见红框处。
第一种情况,线程池的状态已经是STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作队列为空;
第二种情况,工作线程数已经大于最大线程数或当前工作线程已超时,且,还有其他工作线程或任务队列为空 。这点比较难理解,总之先记住,后面会用 。
下面以条件1和条件2分别指代这两种情况的判断条件 。
文章插图
3. 分场景分析线程池回收工作线程3.1 未调用shutdown() ,RUNNING状态下全部任务执行完成的场景这种场景,会将工作线程的数量减少到核心线程数大小(如果本来就没有超过,则不需要回收) 。
比如一个线程池,核心线程数为4,最大线程数为8 。一开始是4个工作线程,当任务把任务队列塞满,就得将工作线程增加到8. 当后面任务执行到差不多了,线程取不到任务了,就会回收到4个工作线程的状态(取决于allowCoreThreadTimeOut的值,这里讨论默认值false的情况,即核心线程不会超时 。如果为true,工作线程可以全部销毁) 。
可以先排除上面提到的条件1,线程池的状态已经是STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作队列为空 。因为线程池一直是RUNNING,这条判断永远是false 。在这个场景中,可以当条件1不存在 。
下面分析取不出任务时线程是怎么运行的 。
step1. 从任务队列取任务有两种方式,超时等待还是可以一直阻塞下去 。决定因素是timed变量 。该变量在前面赋值,如果当前线程数大于核心线程数,变量timed为true, 否则为false(上面说了,这里只讨论allowCoreThreadTimeOut为false的情况) 。很明显,现在讨论的是timed为true的情况 。keepAliveTime一般不设置,默认值为0,所以基本上可以认为是不阻塞,马上返回取任务的结果 。
在线程超时等待唤醒之后,发现取不出任务,timeOut变为true,进入下一次循环 。
step2. 来到条件1的判断,线程池一直RUNNING, 不进入代码块 。
step3. 来到条件2的判断,这时任务队列为空,条件成立,CAS减少线程数,若成功,返回null,否则,重复step1 。
这里要注意,有可能多条线程同时通过条件2的判断,那会不会减少后线程的数量反而比预想的核心线程数少呢?
比如当前线程数已经只有5条了,此时有两条线程同时唤醒,通过条件2的判断,同时减少数量,那剩下的线程数反而只有3条,和预期不一致 。
实际上是不会的 。为了防止这种情况,compareAndDecrementWorkerCount(c) 用的是CAS方法,如果CAS失败就continue,进入下一轮循环,重新判断 。
像上述例子,其中一条线程会CAS失败,然后重新进入循环,发现工作线程数已经只有4了,timed为false, 这条线程就不会被销毁,可以一直阻塞了(workQueue.take()) 。
推荐阅读
- Intel|16线程轻薄本对决!酷睿i5-12500H VS.锐龙7 6800H
- Intel|16线程轻薄本对决!酷睿i5-12500H VS.锐龙7 6800H:混合架构全面碾压
- AMD|384线程只发挥66%实力!AMD Zen4新霄龙就无敌了
- 如何减肥健身?
- AMD|64核心49999元!AMD Zen3线程撕裂者国内开卖 涨价最多30%
- AMD|96核心192线程谁敢一战!AMD Zen4霄龙9654谍照首曝
- AMD|AMD Zen3线程撕裂者零售开卖:64核心4.4万元
- 阴阳师|《征途》全民无差别争霸赛开启试玩 教你在500万现金奖池中捞一笔
- 去除油画的多余颜料 怎么洗掉油画颜料?
- AMD|AMD 64核线程撕裂者火力全开暴超5.15GHz:性能刷新世界纪录