- 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
- 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
突然就懵了!面试官问我:线程池中多余的线程是如何回收的?
2021-10-25
点击上方 关注,?星标或置顶一起成长
免费送 1024GB 精品学习资源
最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也格外通俗易懂,就不再赘述了,别人写的比我好多了。
不过,我倒是对线程池是如何回收工作线程比较感爱好,所以简约分析了一下,加深对线程池的理解吧。
下面以JDK1.8为例进行分析
1.?runWorker(Worker w)
工作线程启动后,就进入runWorker(Worker w)方法。
里面是一个while循环,循环推断任务能否为空,若不为空,执行任务;若取不到任务,或发生特别,退出循环,执行processWorkerExit(w, completedAbruptly); 在这个方法里把工作线程移除掉。
取任务的来源有两个,一个是firstTask,这个是工作线程第一次跑的时候执行的任务,最多只能执行一次,后面得从getTask()方法里取任务。看来,getTask()是关键,在不考虑特别的场景下,前往null,就表示退出循环,结束线程。下一步,就得看看,什么情况下getTask()会前往null。
(篇幅有限,分段截取,省略两头执行任务的步骤)
image
image
2. getTask() 前往null
一共有两种情况会前往null,见红框处 。
第一种情况,线程池的形态已经是STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作队列为空;
其次种情况,工作线程数已经大于最大线程数或当前工作线程已超时,且,还有其他工作线程或任务队列为空。这点比较难理解,总之先记住,后面会用。
下面以条件1和条件2分别指代这两种情况的推断条件。
image
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())。
这一点我思考了很久才得出答案,一直在想没有加锁的情况下是怎样保证肯定能不多不少回收到核心线程数的呢。原来是CAS的奥妙。
从这里也可以看出,虽然有核心线程数,但线程并没有区分是核心还是非核心,并不是先创建的就是核心,超过核心线程数后创建的就是非核心,最终保留哪些线程,完全随机。
3.
文档评论(0)