电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Java線程池中多余的線程是如何回收的

瀏覽:2日期:2022-08-11 17:45:29

最近閱讀了JDK線程池ThreadPoolExecutor的源碼,對(duì)線程池執(zhí)行任務(wù)的流程有了大體了解,實(shí)際上這個(gè)流程也十分通俗易懂,就不再贅述了,別人寫的比我好多了。

不過(guò),我倒是對(duì)線程池是如何回收工作線程比較感興趣,所以簡(jiǎn)單分析了一下,加深對(duì)線程池的理解吧。

那么,就以JDK1.8為例分析吧。

1.runWorker(Worker w)

工作線程啟動(dòng)后,就進(jìn)入runWorker(Worker w)方法。

里面是一個(gè)while循環(huán),循環(huán)判斷任務(wù)是否為空,若不為空,執(zhí)行任務(wù);若取不到任務(wù),或發(fā)生異常,退出循環(huán),執(zhí)行processWorkerExit(w, completedAbruptly); 在這個(gè)方法里把工作線程移除掉。

取任務(wù)的來(lái)源有兩個(gè),一個(gè)是firstTask,這個(gè)是工作線程第一次跑的時(shí)候執(zhí)行的任務(wù),最多只能執(zhí)行一次,后面得從getTask()方法里取任務(wù)。看來(lái),getTask()是關(guān)鍵,在不考慮異常的場(chǎng)景下,返回null,就表示退出循環(huán),結(jié)束線程。下一步,就得看看,什么情況下getTask()會(huì)返回null。

(篇幅有限,分段截取,省略中間執(zhí)行任務(wù)的步驟)

Java線程池中多余的線程是如何回收的

Java線程池中多余的線程是如何回收的

2. getTask() 返回null

一共有兩種情況會(huì)返回null,見(jiàn)紅框處 。

第一種情況,線程池的狀態(tài)已經(jīng)是STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作隊(duì)列為空;

第二種情況,工作線程數(shù)已經(jīng)大于最大線程數(shù)或當(dāng)前工作線程已超時(shí),且,還有其他工作線程或任務(wù)隊(duì)列為空。這點(diǎn)比較難理解,總之先記住,后面會(huì)用。

下面以條件1和條件2分別指代這兩種情況的判斷條件。

Java線程池中多余的線程是如何回收的

3. 分場(chǎng)景分析線程池回收工作線程3.1 未調(diào)用shutdown() ,RUNNING狀態(tài)下全部任務(wù)執(zhí)行完成的場(chǎng)景

這種場(chǎng)景,會(huì)將工作線程的數(shù)量減少到核心線程數(shù)大小(如果本來(lái)就沒(méi)有超過(guò),則不需要回收)。

比如一個(gè)線程池,核心線程數(shù)為4,最大線程數(shù)為8。一開(kāi)始是4個(gè)工作線程,當(dāng)任務(wù)把任務(wù)隊(duì)列塞滿,就得將工作線程增加到8. 當(dāng)后面任務(wù)執(zhí)行到差不多了,線程取不到任務(wù)了,就會(huì)回收到4個(gè)工作線程的狀態(tài)(取決于allowCoreThreadTimeOut的值,這里討論默認(rèn)值false的情況,即核心線程不會(huì)超時(shí)。如果為true,工作線程可以全部銷毀)。

可以先排除上面提到的條件1,線程池的狀態(tài)已經(jīng)是STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作隊(duì)列為空。因?yàn)榫€程池一直是RUNNING,這條判斷永遠(yuǎn)是false。在這個(gè)場(chǎng)景中,可以當(dāng)條件1不存在。

下面分析取不出任務(wù)時(shí)線程是怎么運(yùn)行的。

step1. 從任務(wù)隊(duì)列取任務(wù)有兩種方式,超時(shí)等待還是可以一直阻塞下去。決定因素是timed變量。該變量在前面賦值,如果當(dāng)前線程數(shù)大于核心線程數(shù),變量timed為true, 否則為false(上面說(shuō)了,這里只討論allowCoreThreadTimeOut為false的情況)。很明顯,現(xiàn)在討論的是timed為true的情況。keepAliveTime一般不設(shè)置,默認(rèn)值為0,所以基本上可以認(rèn)為是不阻塞,馬上返回取任務(wù)的結(jié)果。

在線程超時(shí)等待喚醒之后,發(fā)現(xiàn)取不出任務(wù),timeOut變?yōu)閠rue,進(jìn)入下一次循環(huán)。

step2. 來(lái)到條件1的判斷,線程池一直RUNNING, 不進(jìn)入代碼塊。

step3. 來(lái)到條件2的判斷,這時(shí)任務(wù)隊(duì)列為空,條件成立,CAS減少線程數(shù),若成功,返回null,否則,重復(fù)step1。

這里要注意,有可能多條線程同時(shí)通過(guò)條件2的判斷,那會(huì)不會(huì)減少后線程的數(shù)量反而比預(yù)想的核心線程數(shù)少呢?

比如當(dāng)前線程數(shù)已經(jīng)只有5條了,此時(shí)有兩條線程同時(shí)喚醒,通過(guò)條件2的判斷,同時(shí)減少數(shù)量,那剩下的線程數(shù)反而只有3條,和預(yù)期不一致。

實(shí)際上是不會(huì)的。為了防止這種情況,compareAndDecrementWorkerCount(c) 用的是CAS方法,如果CAS失敗就continue,進(jìn)入下一輪循環(huán),重新判斷。

像上述例子,其中一條線程會(huì)CAS失敗,然后重新進(jìn)入循環(huán),發(fā)現(xiàn)工作線程數(shù)已經(jīng)只有4了,timed為false, 這條線程就不會(huì)被銷毀,可以一直阻塞了(workQueue.take())。

這一點(diǎn)我思考了很久才得出答案,一直在想沒(méi)有加鎖的情況下是怎么保證一定能不多不少回收到核心線程數(shù)的呢。原來(lái)是CAS的奧妙。

從這里也可以看出,雖然有核心線程數(shù),但線程并沒(méi)有區(qū)分是核心還是非核心,并不是先創(chuàng)建的就是核心,超過(guò)核心線程數(shù)后創(chuàng)建的就是非核心,最終保留哪些線程,完全隨機(jī)。

3.2 調(diào)用shutdown() ,全部任務(wù)執(zhí)行完成的場(chǎng)景

這種場(chǎng)景,無(wú)論是核心線程還是非核心線程,所有工作線程都會(huì)被銷毀。

在調(diào)用shutdown()之后,會(huì)向所有的空閑工作線程發(fā)送中斷信號(hào)。

Java線程池中多余的線程是如何回收的

最終傳入false,調(diào)用下面這個(gè)方法。

Java線程池中多余的線程是如何回收的

可以看出,在發(fā)出中斷信號(hào)前,會(huì)判斷是否已經(jīng)中斷,以及要獲得工作線程的獨(dú)占鎖。

發(fā)出中斷信號(hào)的時(shí)候,工作線程要么在getTask()里準(zhǔn)備獲取任務(wù),要么在執(zhí)行任務(wù),那就得等它執(zhí)行完當(dāng)前任務(wù)才會(huì)發(fā)出,因?yàn)楣ぷ骶€程在執(zhí)行任務(wù)的時(shí)候,也會(huì)工作線程加鎖。工作線程執(zhí)行完任務(wù),又跑到getTask()里面去了。

所以我們只要看getTask()里面怎么應(yīng)對(duì)中斷異常的就可以了。

Java線程池中多余的線程是如何回收的

工作線程在getTask()里,有兩種可能。

3.2.1 任務(wù)已全部完成,線程在阻塞等待。

很簡(jiǎn)單,中斷信號(hào)將其喚醒,從而進(jìn)入下一輪循環(huán)。到達(dá)條件1處,符合條件,減少工作線程數(shù)量,并返回null,由外層結(jié)束這條線程。

這里的decrementWorkerCount()是自旋式的,一定會(huì)減1。

Java線程池中多余的線程是如何回收的

3.2.2 任務(wù)還沒(méi)有完全執(zhí)行完

調(diào)用shutdown()之后,未執(zhí)行完的任務(wù)要執(zhí)行完畢,池子才能結(jié)束。所以此時(shí)有可能線程還在工作。

這里又要分兩個(gè)階段討論

階段1 任務(wù)較多,工作線程都能獲得任務(wù)

這里還不涉及到線程退出,可以跳過(guò)不看,只是分析一下收到中斷信號(hào)后線程的表現(xiàn)。

假設(shè)有線程A,正通過(guò)getTask()里獲取任務(wù)。此時(shí)A被中斷,在獲取任務(wù)時(shí),無(wú)論是poll()還是take(),都會(huì)拋出中斷異常。異常被捕獲,重新進(jìn)入下一輪循環(huán),只要隊(duì)列不為空,就可以繼續(xù)取任務(wù)。

線程A被中斷,再次取任務(wù),調(diào)用workQueue.poll() or workQueue.take(),不會(huì)拋出異常嗎?還可以正常取出任務(wù)嗎?

這就要看workQueue的實(shí)現(xiàn)了。workQueue是BlockingQueue類型,以常見(jiàn)的LinkedBlockingQueue和ArrayBlockingQueue為例,加鎖時(shí)都是調(diào)用lockInterruptibly(),是響應(yīng)中斷的。該方法又調(diào)用了AQS的acquireInterruptibly(int arg)。

acquireInterruptibly(int arg),無(wú)論是在入口處判斷中斷異常,還是在parkAndCheckInterrupt()方法阻塞,被中斷喚醒并判斷中斷異常時(shí),均使用了Thread.interrupted()。這個(gè)方法會(huì)返回線程的中斷狀態(tài),并把中斷狀態(tài)重置!也就是說(shuō),線程不再是中斷狀態(tài)了,這樣在再次取任務(wù)時(shí),就不會(huì)報(bào)錯(cuò)了。

因此,這對(duì)于正在準(zhǔn)備取任務(wù)的線程,只是相當(dāng)于浪費(fèi)了一次循環(huán),這可能是線程中斷帶來(lái)的副作用吧,當(dāng)然,對(duì)整體的運(yùn)行不影響。

分析到這里,我不禁感嘆,這里BlockingQueue剛好是會(huì)重置中斷狀態(tài),這到底是怎么想出來(lái)的絕妙設(shè)計(jì)啊?Doug Lea大神Orz.

Java線程池中多余的線程是如何回收的

Java線程池中多余的線程是如何回收的

階段2 任務(wù)剛好要執(zhí)行完了

這時(shí)任務(wù)已經(jīng)快取完了,比如有4條工作線程,只剩下2個(gè)任務(wù),那就可能出現(xiàn)2條線程獲得任務(wù),2條線程阻塞。

因?yàn)樵讷@取任務(wù)前的判斷,沒(méi)有加鎖,那么會(huì)不會(huì)出現(xiàn),所有線程都通過(guò)了前面的校驗(yàn),來(lái)到workQueue獲取任務(wù)的地方,剛好任務(wù)隊(duì)列已經(jīng)空了,線程全部阻塞了呢?因?yàn)閟hutdown() 已經(jīng)執(zhí)行完畢,無(wú)法再向線程發(fā)出中斷信號(hào),從而線程一直在阻塞,無(wú)法被回收。

這種是不會(huì)發(fā)生的。

假設(shè)有A,B,C,D四條工作線程,同時(shí)通過(guò)了條件1和條件2的判斷,來(lái)到取任務(wù)的地方。那么,工作隊(duì)列至少還有一個(gè)任務(wù),至少會(huì)有一條線程能取到任務(wù)。

假設(shè)A,B獲得了任務(wù),C,D阻塞。

A, B接下來(lái)的步驟是:

step1.任務(wù)執(zhí)行完成后,再次getTask(),此時(shí)符合條件1,返回null,線程準(zhǔn)備被回收。

step2.processWorkerExit(Worker w, boolean completedAbruptly) 將線程回收。

回收就只是把線程干掉這么簡(jiǎn)單嗎?來(lái)看看processWorkerExit(Worker w, boolean completedAbruptly) 的方法。

Java線程池中多余的線程是如何回收的

可以看到,在里面除了workers.remove(w) 移除線,還調(diào)用了tryTerminate()。

Java線程池中多余的線程是如何回收的

第一個(gè)判斷條件沒(méi)有一個(gè)子條件符合,跳過(guò)。第二個(gè)條件,工作線程還存在,那么隨機(jī)中斷一條空閑線程。

那么問(wèn)題就來(lái)了,中斷一條空閑線程,也沒(méi)說(shuō)是一定中斷正在阻塞的線程啊。如果A, B同時(shí)退出,有沒(méi)有可能出現(xiàn)A中斷B, B中斷A,AB互相中斷,從而沒(méi)有線程去中斷喚醒阻塞的線程呢?

答案仍然是,想多了……

假設(shè)A能走到這里,說(shuō)明A已經(jīng)從工作線程的集合workers里面移除了(processWorkerExit(Worker w, boolean completedAbruptly) 在tryTerminate()之前,已經(jīng)將其移除)。那么A中斷B,B來(lái)到這里中斷,就不會(huì)在workers里面找到A了。

Java線程池中多余的線程是如何回收的

也就是說(shuō),退出的線程不能互相中斷,我從集合中退出后,中斷了你,你不能中斷我,因?yàn)槲乙呀?jīng)退出集合,你只能中斷別人。那么,即使有N個(gè)線程同時(shí)退出,至少在最后,也會(huì)有一條線程,會(huì)中斷剩余的阻塞線程。

就像多米諾骨牌一樣,中斷信號(hào)就會(huì)被傳播下去。

阻塞的C,D中的任意一條被中斷喚醒后,又會(huì)重復(fù)step1的動(dòng)作,周而復(fù)始,直到所有阻塞線程都被中斷,喚醒。

這也是為什么在tryTerminate()里面,傳入false,只需要中斷任意一條空閑線程的原因。

想到這里,再次對(duì)Doug Lea心生欽敬(粵語(yǔ))之情。這設(shè)計(jì)得也太妙了叭。

4. 總結(jié)

ThreadPoolExecutor回收工作線程,一條線程getTask()返回null,就會(huì)被回收。

分兩種場(chǎng)景。

1) 未調(diào)用shutdown() ,RUNNING狀態(tài)下全部任務(wù)執(zhí)行完成的場(chǎng)景

線程數(shù)量大于corePoolSize,線程超時(shí)阻塞,超時(shí)喚醒后CAS減少工作線程數(shù),如果CAS成功,返回null,線程回收。否則進(jìn)入下一次循環(huán)。當(dāng)工作者線程數(shù)量小于等于corePoolSize,就可以一直阻塞了。

2) 調(diào)用shutdown() ,全部任務(wù)執(zhí)行完成的場(chǎng)景

shutdown() 會(huì)向所有線程發(fā)出中斷信號(hào),這時(shí)有兩種可能。

2.1)所有線程都在阻塞

中斷喚醒,進(jìn)入循環(huán),都符合第一個(gè)if判斷條件,都返回null,所有線程回收。

2.2)任務(wù)還沒(méi)有完全執(zhí)行完

至少會(huì)有一條線程被回收。在processWorkerExit(Worker w, boolean completedAbruptly)方法里會(huì)調(diào)用tryTerminate(),向任意空閑線程發(fā)出中斷信號(hào)。所有被阻塞的線程,最終都會(huì)被一個(gè)個(gè)喚醒,回收。

這一次的分析,昨晚開(kāi)始寫,寫到一半卡殼,今天早上接著寫,前后花了大概2+2=4個(gè)小時(shí)寫博客以及1小時(shí)思考。

說(shuō)實(shí)話自己還是有點(diǎn)亂,無(wú)法一下子理解透徹,也不知道自己理解得對(duì)不對(duì)。

有沒(méi)有用,我也不知道,只能說(shuō),加深了對(duì)線程池的理解吧(安慰自己),同時(shí)也感慨設(shè)計(jì)之精妙。

到此這篇關(guān)于Java線程池中多余的線程是如何回收的的文章就介紹到這了,更多相關(guān)Java 多余線程回收內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: jrs高清nba(无插件)直播-jrs直播低调看直播-jrs直播nba-jrs直播 上海地磅秤|电子地上衡|防爆地磅_上海地磅秤厂家–越衡称重 | 科箭WMS仓库管理软件-TMS物流管理系统-科箭SaaS云服务 | 开锐教育-学历提升-职称评定-职业资格培训-积分入户 | 球磨机 选矿球磨机 棒磨机 浮选机 分级机 选矿设备厂家 | 集装箱标准养护室-集装箱移动式养护室-广州璟业试验仪器有限公司 | 天津次氯酸钠酸钙溶液-天津氢氧化钠厂家-天津市辅仁化工有限公司 | 精雕机-火花机-精雕机 cnc-高速精雕机-电火花机-广东鼎拓机械科技有限公司 | 光栅尺厂家_数显表维修-苏州泽升精密机械 | 选矿设备-新型重选设备-金属矿尾矿重选-青州冠诚重工机械有限公司 | Win10系统下载_32位/64位系统/专业版/纯净版下载 | 高精度-恒温冷水机-螺杆式冰水机-蒸发冷冷水机-北京蓝海神骏科技有限公司 | 电磁流量计厂家_涡街流量计厂家_热式气体流量计-青天伟业仪器仪表有限公司 | 周口风机|周风风机|河南省周口通用风机厂 | 上海租奔驰_上海租商务车_上海租车网-矢昂汽车服务公司 | 北京网站建设公司_北京网站制作公司_北京网站设计公司-北京爱品特网站建站公司 | 可程式恒温恒湿试验箱|恒温恒湿箱|恒温恒湿试验箱|恒温恒湿老化试验箱|高低温试验箱价格报价-广东德瑞检测设备有限公司 | MES系统-WMS系统-MES定制开发-制造执行MES解决方案-罗浮云计算 | 环讯传媒,永康网络公司,永康网站建设,永康小程序开发制作,永康网站制作,武义网页设计,金华地区网站SEO优化推广 - 永康市环讯电子商务有限公司 | 净水器代理,净水器招商,净水器加盟-FineSky德国法兹全屋净水 | 宝宝药浴-产后药浴-药浴加盟-艾裕-专注母婴调养泡浴 | 超声波乳化机-超声波分散机|仪-超声波萃取仪-超声波均质机-精浩机械|首页 | 爆破器材运输车|烟花爆竹运输车|1-9类危险品厢式运输车|湖北江南专用特种汽车有限公司 | 微水泥_硅藻泥_艺术涂料_艺术漆_艺术漆加盟-青岛泥之韵环保壁材 武汉EPS线条_EPS装饰线条_EPS构件_湖北博欧EPS线条厂家 | 布袋式除尘器|木工除尘器|螺旋输送机|斗式提升机|刮板输送机|除尘器配件-泊头市德佳环保设备 | 应急灯_消防应急灯_应急照明灯_应急灯厂家-大成智慧官网 | 小型气象站_车载气象站_便携气象站-山东风途物联网 | 咖啡加盟-咖啡店加盟-咖啡西餐厅加盟-塞纳左岸咖啡西餐厅官网 | 高速龙门架厂家_监控杆_多功能灯杆_信号灯杆_锂电池太阳能路灯-鑫世源照明 | 房间温控器|LonWorks|海思| 税筹星_灵活用工平台_企业财务顾问_财税法薪综合服务平台 | 电动卫生级调节阀,电动防爆球阀,电动软密封蝶阀,气动高压球阀,气动对夹蝶阀,气动V型调节球阀-上海川沪阀门有限公司 | 考勤系统_人事考勤管理系统_本地部署BS考勤系统_考勤软件_天时考勤管理专家 | 安平县鑫川金属丝网制品有限公司,防风抑尘网,单峰防风抑尘,不锈钢防风抑尘网,铝板防风抑尘网,镀铝锌防风抑尘网 | 深圳市索富通实业有限公司-可燃气体报警器 | 可燃气体探测器 | 气体检测仪 | 儋州在线-儋州招聘找工作、找房子、找对象,儋州综合生活信息门户! | 机制砂选粉机_砂石选粉机厂家-盐城市助成粉磨科技有限公司 | 懂研帝_专业SCI论文润色机构_SCI投稿发表服务公司 | 湖州织里童装_女童男童中大童装_款式多尺码全_织里儿童网【官网】-嘉兴嘉乐网络科技有限公司 | 手术室净化厂家_成都实验室装修公司_无尘车间施工单位_洁净室工程建设团队-四川华锐16年行业经验 | 学习虾-免费的学习资料下载平台 雪花制冰机(实验室雪花制冰机)百科 | 大功率金属激光焊接机价格_不锈钢汽车配件|光纤自动激光焊接机设备-东莞市正信激光科技有限公司 定制奶茶纸杯_定制豆浆杯_广东纸杯厂_[绿保佳]一家专业生产纸杯碗的厂家 |