摘要:參數(shù)說(shuō)明,線(xiàn)程池保留的最小線(xiàn)程數(shù)。,線(xiàn)程池中允許擁有的最大線(xiàn)程數(shù)。,線(xiàn)程池的運(yùn)行狀態(tài)。除非線(xiàn)程池狀態(tài)發(fā)生了變化,發(fā)退回到外層循環(huán)重新執(zhí)行,判斷線(xiàn)程池的狀態(tài)。是線(xiàn)程池的核心控制狀態(tài),包含的線(xiàn)程池運(yùn)行狀態(tài)和有效線(xiàn)程數(shù)。
Java是一門(mén)多線(xiàn)程的語(yǔ)言,基本上生產(chǎn)環(huán)境的Java項(xiàng)目都離不開(kāi)多線(xiàn)程。而線(xiàn)程則是其中最重要的系統(tǒng)資源之一,如果這個(gè)資源利用得不好,很容易導(dǎo)致程序低效率,甚至是出問(wèn)題。
有以下場(chǎng)景,有個(gè)電話(huà)撥打系統(tǒng),有一堆需要撥打的任務(wù)要執(zhí)行,首先肯定是考慮多線(xiàn)程異步去執(zhí)行。假如我每執(zhí)行一個(gè)撥打任務(wù)都new一個(gè)Thread去執(zhí)行,當(dāng)同時(shí)有1萬(wàn)個(gè)任務(wù)需要執(zhí)行的時(shí)候,那么就會(huì)新建1萬(wàn)個(gè)線(xiàn)程,加上線(xiàn)程各種初始銷(xiāo)毀等操作,這個(gè)消耗是巨大的。而其實(shí)往往實(shí)現(xiàn)這些功能的時(shí)候,并不是完全需要實(shí)時(shí)馬上完成,只是希望在可控范圍內(nèi)盡量提高執(zhí)行的并發(fā)性能。
因此線(xiàn)程池技術(shù)應(yīng)用而生,Java中最常用的線(xiàn)程池技術(shù)就是ThreadPoolExecutor。接下來(lái)就整體看看ThreadPoolExecutor的實(shí)現(xiàn)。
這個(gè)類(lèi)的注解非常多,很多也是重點(diǎn),所以就不從注解開(kāi)始看起。先從使用說(shuō)起,有個(gè)概念先。
基本使用// 核心線(xiàn)程 int corePoolSize = 5; // 最大線(xiàn)程 int maximumPoolSize = 10; // 線(xiàn)程空閑回收時(shí)間 int keepAliveTime = 30; // 線(xiàn)程空閑回調(diào)時(shí)間單位 TimeUnit unit = TimeUnit.SECONDS; // 隊(duì)列大小 int queueSize = 20; // 隊(duì)列 BlockingQueue workQueue = new ArrayBlockingQueue(queueSize); ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); executor.execute(() -> { // do something 1 }); executor.execute(() -> { // do something 2 });
定義好一些必要的參數(shù),構(gòu)建一個(gè)ThreadPoolExecutor對(duì)象。然后調(diào)用對(duì)象的execute()方法即可。
參數(shù)說(shuō)明:
corePoolSize,線(xiàn)程池保留的最小線(xiàn)程數(shù)。如果線(xiàn)程池中的線(xiàn)程少于此數(shù)目,則在執(zhí)行execut()時(shí)創(chuàng)建。
maximumPoolSize,線(xiàn)程池中允許擁有的最大線(xiàn)程數(shù)。
keepAliveTime、unit,當(dāng)線(xiàn)程閑置時(shí),保持線(xiàn)程存活的時(shí)間。
workQueue,工作隊(duì)列,存放提交的等待任務(wù),其中有隊(duì)列大小的限制。
線(xiàn)程管理機(jī)制非常多人誤解了corePoolSize、maximumPoolSize、workQueue的相互關(guān)系。不少人認(rèn)為無(wú)論隊(duì)列選擇什么,corePoolSize和maximumPoolSize一定是有用,定義一定是生效的,其實(shí)并不然啊!
看下線(xiàn)程基本規(guī)則注解說(shuō)明
默認(rèn)情況下,線(xiàn)程池在初始的時(shí)候,線(xiàn)程數(shù)為0。當(dāng)接收到一個(gè)任務(wù)時(shí),如果線(xiàn)程池中存活的線(xiàn)程數(shù)小于corePoolSize核心線(xiàn)程,則新建一個(gè)線(xiàn)程。
如果所有運(yùn)行的核心線(xiàn)程都都在忙,超出核心線(xiàn)程處理的任務(wù),執(zhí)行器更多地選擇把任務(wù)放進(jìn)隊(duì)列,而不是新建一個(gè)線(xiàn)程。
如果一個(gè)任務(wù)提交不了到隊(duì)列,在不超出最大線(xiàn)程數(shù)量情況下,會(huì)新建線(xiàn)程。超出了就會(huì)報(bào)錯(cuò)。
另外,如果想在線(xiàn)程初始化時(shí)候就有核心線(xiàn)程,可以調(diào)用prestartCoreThread()或prestartAllCoreThread(),前者是初始一個(gè),后者是初始全部。
再看看排隊(duì)策略
直接提交,用SynchronousQueue。特點(diǎn)是不保存,直接提交給線(xiàn)程,如果沒(méi)沒(méi)線(xiàn)程,則新建一個(gè)。
無(wú)限提交,用類(lèi)似LinkedBlockingQueue無(wú)界隊(duì)列。特點(diǎn)是保存所以核心線(xiàn)程處理不了的任務(wù),隊(duì)列無(wú)上限,最大線(xiàn)程也沒(méi)用。
有限提交,用類(lèi)似ArrayBlockingQueue有界隊(duì)列。特點(diǎn)是可以保存超過(guò)核心線(xiàn)程的任務(wù),并且隊(duì)列也是有上限的。超過(guò)上限,新建線(xiàn)程(滿(mǎn)了拋錯(cuò))。更好地保護(hù)資源,防止崩潰,也是最常用的排隊(duì)策略。
從以上規(guī)則可以看出來(lái),核心線(xiàn)程數(shù)和最大線(xiàn)程數(shù),還有隊(duì)列結(jié)構(gòu)是相互影響的,如何排隊(duì),隊(duì)列多大,最大線(xiàn)程是多少都是不一定的。
再看看保持存活機(jī)制
當(dāng)超過(guò)核心線(xiàn)程數(shù)的線(xiàn)程,線(xiàn)程池會(huì)讓該線(xiàn)程保持存活keepAliveTime時(shí)間,超過(guò)該時(shí)間則會(huì)銷(xiāo)毀該線(xiàn)程。
另外默認(rèn)對(duì)非核心線(xiàn)程有效,若想核心線(xiàn)程也適用于這個(gè)機(jī)制,可以調(diào)用allowCoreThreadTimeOut()方法。這樣的話(huà)就沒(méi)有核心線(xiàn)程這一說(shuō)了。
綜合以上,線(xiàn)程池在多次執(zhí)行任務(wù)后,會(huì)一直維持部分線(xiàn)程存活,即使它是閑置的。這樣的目的是為了減少線(xiàn)程銷(xiāo)毀創(chuàng)建的開(kāi)銷(xiāo),下次有個(gè)任務(wù)需要執(zhí)行,直接從池子里拿線(xiàn)程就能用了。但核心線(xiàn)程不能維護(hù)太多,因?yàn)橐残枰欢ㄩ_(kāi)銷(xiāo)。最大的線(xiàn)程數(shù)保護(hù)了整個(gè)系統(tǒng)的穩(wěn)定性,避免并發(fā)量大的時(shí)候,把線(xiàn)程擠滿(mǎn)。工作隊(duì)列則是保證了任務(wù)順序和暫存,系統(tǒng)的可靠性。線(xiàn)程存活規(guī)則的目的和維護(hù)核心線(xiàn)程的目的類(lèi)似,但降低了它的存活的時(shí)間。
另外還有拒絕機(jī)制,它提供了一些異常情況下的解決方案。
這個(gè)ctl變量是整個(gè)線(xiàn)程池的核心控制狀態(tài)。
這個(gè)ctl代表了兩個(gè)變量
workerCount,生效的線(xiàn)程數(shù)。基本可以理解為存活的線(xiàn)程,但某個(gè)時(shí)候有暫時(shí)性的差異。
runState,線(xiàn)程池的運(yùn)行狀態(tài)。
其中,ctl(int32位)的低29位代表workerCount,所以最大線(xiàn)程數(shù)為(2^29)-1。另外3位表示runState。
runState有以下幾種狀態(tài):
RUNNING:接收新任務(wù),處理隊(duì)列任務(wù)。
SHUTDOWN:不接收新任務(wù),但處理隊(duì)列任務(wù)。
STOP:不接收新任務(wù),也不處理隊(duì)列任務(wù),并且中斷所有處理中的任務(wù)。
TIDYING:所有任務(wù)都被終結(jié),有效線(xiàn)程為0。會(huì)觸發(fā)terminated()方法。
TERMINATED:當(dāng)terminated()方法執(zhí)行結(jié)束。
當(dāng)調(diào)用了shutdown(),狀態(tài)會(huì)從RUNNING變成SHUTDOWN,不再接收新任務(wù),此時(shí)會(huì)處理完隊(duì)列里面的任務(wù)。
如果調(diào)用的是shutdownNow(),狀態(tài)會(huì)直接變成STOP。
當(dāng)線(xiàn)程或者隊(duì)列都是空的時(shí)候,狀態(tài)就會(huì)變成TIDYING。
當(dāng)terminated()執(zhí)行完的時(shí)候,就會(huì)變成TERMINATED。
帶著對(duì)上面的規(guī)則與機(jī)制的認(rèn)識(shí),現(xiàn)在從就這這個(gè)入口開(kāi)始看看源碼,到底整個(gè)流程是怎么實(shí)現(xiàn)的。
如果少于核心線(xiàn)程在跑,用這個(gè)任務(wù)嘗試創(chuàng)建一個(gè)新線(xiàn)程。
如果一個(gè)任務(wù)成功入隊(duì),再次檢查下線(xiàn)程池狀態(tài)看是否需要入隊(duì),因?yàn)榭赡茉谌腙?duì)過(guò)程中,狀態(tài)發(fā)送了變化。如果確認(rèn)入隊(duì)且沒(méi)有存活線(xiàn)程,則新建一個(gè)空線(xiàn)程。
如果進(jìn)不了隊(duì),則嘗試新建一個(gè)線(xiàn)程,如果都失敗了。拒絕這個(gè)task
對(duì)于第二點(diǎn)最后為什么新建一個(gè)線(xiàn)程?很容易猜想到,會(huì)有一個(gè)輪詢(xún)的機(jī)制讓下個(gè)task出隊(duì),直接利用這個(gè)空閑線(xiàn)程。
注釋基本解釋了所有代碼,代碼也沒(méi)什么特別的。其中最主要的還是addWoker()這個(gè)方法,下面來(lái)看看。
addWoker()先了解下這個(gè)方法的整體思路
從描述可知,addwoker失敗,會(huì)在線(xiàn)程池狀態(tài)不對(duì)、線(xiàn)程滿(mǎn)了或者線(xiàn)程工廠創(chuàng)建線(xiàn)程池失敗時(shí)候發(fā)生。
這個(gè)方法比較長(zhǎng),分兩段看。先看第一段。
retry:這種寫(xiě)法,如果比較少看源碼的,應(yīng)該是前所未見(jiàn)的了。這是個(gè)循環(huán)的位置標(biāo)記,是java的語(yǔ)法之一。看回代碼,這里面for循環(huán)還嵌套里一個(gè)for循環(huán),而retry:是標(biāo)記第一個(gè)for循環(huán)的,后面break和continue語(yǔ)句都指向到了retry。說(shuō)明break和continue是都是操作外層的for循環(huán)。retry可以是任何變量命名合法的字符。
然后看看外出for循環(huán)的if語(yǔ)句
這個(gè)if判斷想要執(zhí)行到return false;,隊(duì)列為空是一個(gè)必要條件。因?yàn)閍ddWork()不單只接收新任務(wù)會(huì)調(diào)用到,處理隊(duì)列中的任務(wù)也會(huì)調(diào)用到。而前面提到SHUTDOWN狀態(tài)下還會(huì)處理隊(duì)列中的任務(wù)的,所以隊(duì)列不為空是會(huì)讓它繼續(xù)執(zhí)行下去的。
對(duì)于內(nèi)層的for循環(huán)
會(huì)先判斷worker的數(shù)據(jù)是否符合corePoolSize和maximumPoolSize的定義,不滿(mǎn)足則返回失敗。
然后嘗試CAS讓workerCount自增,如果CAS失敗還是繼續(xù)自旋去自增,直到成功。除非線(xiàn)程池狀態(tài)發(fā)生了變化,發(fā)退回到外層for循環(huán)重新執(zhí)行,判斷線(xiàn)程池的狀態(tài)。
第一段的代碼,就是讓workerCount在符合條件下自增
第二段代碼
這段比較好理解,先創(chuàng)建一個(gè)Worker對(duì)象,這個(gè)Worker里面包含一個(gè)由線(xiàn)程工廠創(chuàng)建的線(xiàn)程,和一個(gè)需要執(zhí)行的任務(wù)(可以為空)。如果線(xiàn)程創(chuàng)建成功了,那么就加一個(gè)重入鎖去把這個(gè)新建的Worker對(duì)象放到workers成員變量中,在加入之前需要重新判斷下線(xiàn)程池的狀態(tài)和新建線(xiàn)程的狀態(tài)。如果worker添加到workers成員變量中,就啟動(dòng)這個(gè)新建的線(xiàn)程。最后如果添加失敗,則執(zhí)行addWorkFailed(w)。
如果失敗了,加鎖操作回滾下wokers、workerCount,然后判斷下?tīng)顟B(tài)看看是否需要終結(jié)線(xiàn)程池。
addWorker()大概的流程就這樣。
總結(jié)對(duì)于其他方法,沒(méi)有什么特別的,在此不再過(guò)多的敘述,有興趣的可以翻翻源碼閱讀下。
回顧總結(jié)下上面的核心要點(diǎn)
當(dāng)核心線(xiàn)程滿(mǎn)且忙碌時(shí),線(xiàn)程池傾向于把提交的任務(wù)放進(jìn)隊(duì)列,而不是新建線(xiàn)程。
根據(jù)選擇隊(duì)列的不同,maximumPoolSize不一定有用的。具體有三種不同的策略。
ctl是線(xiàn)程池的核心控制狀態(tài),包含的runState線(xiàn)程池運(yùn)行狀態(tài)和workCount有效線(xiàn)程數(shù)。
retry:是一種標(biāo)記循環(huán)的語(yǔ)法,retry可以是任何變量命名合法字符。
如果覺(jué)得還不錯(cuò),請(qǐng)關(guān)注公眾號(hào):Zack說(shuō)碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77103.html
摘要:用于限定中線(xiàn)程數(shù)的最大值。該線(xiàn)程池中的任務(wù)隊(duì)列維護(hù)著等待執(zhí)行的對(duì)象。線(xiàn)程池和消息隊(duì)列筆者在實(shí)際工程應(yīng)用中,使用過(guò)多線(xiàn)程和消息隊(duì)列處理過(guò)異步任務(wù)。以上是筆者在學(xué)習(xí)實(shí)踐之后對(duì)于多線(xiàn)程和消息隊(duì)列的粗淺認(rèn)識(shí),初學(xué)者切莫混淆兩者的作用。 多線(xiàn)程編程很難,難點(diǎn)在于多線(xiàn)程代碼的執(zhí)行不是按照我們直覺(jué)上的執(zhí)行順序。所以多線(xiàn)程編程必須要建立起一個(gè)宏觀的認(rèn)識(shí)。 線(xiàn)程池是多線(xiàn)程編程中的一個(gè)重要概念。為了能夠更...
摘要:本文是作者自己對(duì)中線(xiàn)程的狀態(tài)線(xiàn)程間協(xié)作相關(guān)使用的理解與總結(jié),不對(duì)之處,望指出,共勉。當(dāng)中的的數(shù)目而不是已占用的位置數(shù)大于集合番一文通版集合番一文通版垃圾回收機(jī)制講得很透徹,深入淺出。 一小時(shí)搞明白自定義注解 Annotation(注解)就是 Java 提供了一種元程序中的元素關(guān)聯(lián)任何信息和著任何元數(shù)據(jù)(metadata)的途徑和方法。Annotion(注解) 是一個(gè)接口,程序可以通過(guò)...
摘要:所以說(shuō)我們的線(xiàn)程最好是交由線(xiàn)程池來(lái)管理,這樣可以減少對(duì)線(xiàn)程生命周期的管理,一定程度上提高性能。線(xiàn)程池不接收新任務(wù),不處理已添加的任務(wù),并且會(huì)中斷正在處理的任務(wù)。當(dāng)所有的任務(wù)已終止,記錄的任務(wù)數(shù)量為,線(xiàn)程池會(huì)變?yōu)闋顟B(tài)。線(xiàn)程池徹底終止的狀態(tài)。 前言 只有光頭才能變強(qiáng) 回顧前面: ThreadLocal就是這么簡(jiǎn)單 多線(xiàn)程三分鐘就可以入個(gè)門(mén)了! 多線(xiàn)程基礎(chǔ)必要知識(shí)點(diǎn)!看了學(xué)習(xí)多線(xiàn)程事半功倍...
閱讀 2322·2021-11-24 09:39
閱讀 3054·2021-10-15 09:39
閱讀 3106·2021-07-26 23:38
閱讀 2301·2019-08-30 11:14
閱讀 3419·2019-08-29 16:39
閱讀 1722·2019-08-29 15:23
閱讀 791·2019-08-29 13:01
閱讀 2673·2019-08-29 12:29