溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么理解ThreadPoolExecutor線程池技術(shù)

發(fā)布時間:2021-10-12 10:39:32 來源:億速云 閱讀:131 作者:柒染 欄目:云計算

本篇文章為大家展示了怎么理解ThreadPoolExecutor線程池技術(shù),內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

Java是一門多線程的語言,基本上生產(chǎn)環(huán)境的Java項目都離不開多線程。而線程則是其中最重要的系統(tǒng)資源之一,如果這個資源利用得不好,很容易導(dǎo)致程序低效率,甚至是出問題。

有以下場景,有個電話撥打系統(tǒng),有一堆需要撥打的任務(wù)要執(zhí)行,首先肯定是考慮多線程異步去執(zhí)行。假如我每執(zhí)行一個撥打任務(wù)都new一個Thread去執(zhí)行,當(dāng)同時有1萬個任務(wù)需要執(zhí)行的時候,那么就會新建1萬個線程,加上線程各種初始銷毀等操作,這個消耗是巨大的。而其實(shí)往往實(shí)現(xiàn)這些功能的時候,并不是完全需要實(shí)時馬上完成,只是希望在可控范圍內(nèi)盡量提高執(zhí)行的并發(fā)性能。

因此線程池技術(shù)應(yīng)用而生,Java中最常用的線程池技術(shù)就是ThreadPoolExecutor。接下來就整體看看ThreadPoolExecutor的實(shí)現(xiàn)。 這個類的注解非常多,很多也是重點(diǎn),所以就不從注解開始看起。先從使用說起,有個概念先。

基本使用

        // 核心線程
        int corePoolSize = 5;
        // 最大線程
        int maximumPoolSize = 10;
        // 線程空閑回收時間
        int keepAliveTime = 30;
        // 線程空閑回調(diào)時間單位
        TimeUnit unit = TimeUnit.SECONDS;
        // 隊列大小
        int queueSize = 20;
        // 隊列
        BlockingQueue workQueue = new ArrayBlockingQueue<Runnable>(queueSize);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        executor.execute(() -> {
            // do something 1
        });
        executor.execute(() -> {
            // do something 2
        });

定義好一些必要的參數(shù),構(gòu)建一個ThreadPoolExecutor對象。然后調(diào)用對象的execute()方法即可。 參數(shù)說明:

  • corePoolSize,線程池保留的最小線程數(shù)。如果線程池中的線程少于此數(shù)目,則在執(zhí)行execut()時創(chuàng)建。

  • maximumPoolSize,線程池中允許擁有的最大線程數(shù)。

  • keepAliveTime、unit,當(dāng)線程閑置時,保持線程存活的時間。

  • workQueue,工作隊列,存放提交的等待任務(wù),其中有隊列大小的限制。

線程管理機(jī)制

非常多人誤解了corePoolSize、maximumPoolSize、workQueue的相互關(guān)系。不少人認(rèn)為無論隊列選擇什么,corePoolSize和maximumPoolSize一定是有用,定義一定是生效的,其實(shí)并不然啊!

看下線程基本規(guī)則注解說明

怎么理解ThreadPoolExecutor線程池技術(shù)

  1. 默認(rèn)情況下,線程池在初始的時候,線程數(shù)為0。當(dāng)接收到一個任務(wù)時,如果線程池中存活的線程數(shù)小于corePoolSize核心線程,則新建一個線程。

  2. 如果所有運(yùn)行的核心線程都都在忙,超出核心線程處理的任務(wù),執(zhí)行器更多地選擇把任務(wù)放進(jìn)隊列,而不是新建一個線程。

  3. 如果一個任務(wù)提交不了到隊列,在不超出最大線程數(shù)量情況下,會新建線程。超出了就會報錯。

另外,如果想在線程初始化時候就有核心線程,可以調(diào)用prestartCoreThread()或prestartAllCoreThread(),前者是初始一個,后者是初始全部。

再看看排隊策略

怎么理解ThreadPoolExecutor線程池技術(shù)

  • 直接提交,用SynchronousQueue。特點(diǎn)是不保存,直接提交給線程,如果沒沒線程,則新建一個。

  • 無限提交,用類似LinkedBlockingQueue無界隊列。特點(diǎn)是保存所以核心線程處理不了的任務(wù),隊列無上限,最大線程也沒用。

  • 有限提交,用類似ArrayBlockingQueue有界隊列。特點(diǎn)是可以保存超過核心線程的任務(wù),并且隊列也是有上限的。超過上限,新建線程(滿了拋錯)。更好地保護(hù)資源,防止崩潰,也是最常用的排隊策略。

從以上規(guī)則可以看出來,核心線程數(shù)和最大線程數(shù),還有隊列結(jié)構(gòu)是相互影響的,如何排隊,隊列多大,最大線程是多少都是不一定的。

再看看保持存活機(jī)制

怎么理解ThreadPoolExecutor線程池技術(shù)

當(dāng)超過核心線程數(shù)的線程,線程池會讓該線程保持存活keepAliveTime時間,超過該時間則會銷毀該線程。 另外默認(rèn)對非核心線程有效,若想核心線程也適用于這個機(jī)制,可以調(diào)用allowCoreThreadTimeOut()方法。這樣的話就沒有核心線程這一說了。

綜合以上,線程池在多次執(zhí)行任務(wù)后,會一直維持部分線程存活,即使它是閑置的。這樣的目的是為了減少線程銷毀創(chuàng)建的開銷,下次有個任務(wù)需要執(zhí)行,直接從池子里拿線程就能用了。但核心線程不能維護(hù)太多,因?yàn)橐残枰欢ㄩ_銷。最大的線程數(shù)保護(hù)了整個系統(tǒng)的穩(wěn)定性,避免并發(fā)量大的時候,把線程擠滿。工作隊列則是保證了任務(wù)順序和暫存,系統(tǒng)的可靠性。線程存活規(guī)則的目的和維護(hù)核心線程的目的類似,但降低了它的存活的時間。

另外還有拒絕機(jī)制,它提供了一些異常情況下的解決方案。

怎么理解ThreadPoolExecutor線程池技術(shù)

ctl線程狀態(tài)控制

這個ctl變量是整個線程池的核心控制狀態(tài)。

怎么理解ThreadPoolExecutor線程池技術(shù)

這個ctl代表了兩個變量

  • workerCount,生效的線程數(shù)?;究梢岳斫鉃榇婊畹木€程,但某個時候有暫時性的差異。

  • runState,線程池的運(yùn)行狀態(tài)。 其中,ctl(int32位)的低29位代表workerCount,所以最大線程數(shù)為(2^29)-1。另外3位表示runState。

runState有以下幾種狀態(tài):

怎么理解ThreadPoolExecutor線程池技術(shù)

  • RUNNING:接收新任務(wù),處理隊列任務(wù)。

  • SHUTDOWN:不接收新任務(wù),但處理隊列任務(wù)。

  • STOP:不接收新任務(wù),也不處理隊列任務(wù),并且中斷所有處理中的任務(wù)。

  • TIDYING:所有任務(wù)都被終結(jié),有效線程為0。會觸發(fā)terminated()方法。

  • TERMINATED:當(dāng)terminated()方法執(zhí)行結(jié)束。

當(dāng)調(diào)用了shutdown(),狀態(tài)會從RUNNING變成SHUTDOWN,不再接收新任務(wù),此時會處理完隊列里面的任務(wù)。 如果調(diào)用的是shutdownNow(),狀態(tài)會直接變成STOP。 當(dāng)線程或者隊列都是空的時候,狀態(tài)就會變成TIDYING。 當(dāng)terminated()執(zhí)行完的時候,就會變成TERMINATED。

execute()

帶著對上面的規(guī)則與機(jī)制的認(rèn)識,現(xiàn)在從就這這個入口開始看看源碼,到底整個流程是怎么實(shí)現(xiàn)的。

怎么理解ThreadPoolExecutor線程池技術(shù)

  1. 如果少于核心線程在跑,用這個任務(wù)嘗試創(chuàng)建一個新線程。

  2. 如果一個任務(wù)成功入隊,再次檢查下線程池狀態(tài)看是否需要入隊,因?yàn)榭赡茉谌腙犨^程中,狀態(tài)發(fā)送了變化。如果確認(rèn)入隊且沒有存活線程,則新建一個空線程。

  3. 如果進(jìn)不了隊,則嘗試新建一個線程,如果都失敗了。拒絕這個task

對于第二點(diǎn)最后為什么新建一個線程?很容易猜想到,會有一個輪詢的機(jī)制讓下個task出隊,直接利用這個空閑線程。

注釋基本解釋了所有代碼,代碼也沒什么特別的。其中最主要的還是addWoker()這個方法,下面來看看。

addWoker()

先了解下這個方法的整體思路

怎么理解ThreadPoolExecutor線程池技術(shù)

從描述可知,addwoker失敗,會在線程池狀態(tài)不對、線程滿了或者線程工廠創(chuàng)建線程池失敗時候發(fā)生。 這個方法比較長,分兩段看。先看第一段。

怎么理解ThreadPoolExecutor線程池技術(shù)

retry:這種寫法,如果比較少看源碼的,應(yīng)該是前所未見的了。這是個循環(huán)的位置標(biāo)記,是java的語法之一??椿卮a,這里面for循環(huán)還嵌套里一個for循環(huán),而retry:是標(biāo)記第一個for循環(huán)的,后面breakcontinue語句都指向到了retry。說明breakcontinue是都是操作外層的for循環(huán)。retry可以是任何變量命名合法的字符。

然后看看外出for循環(huán)的if語句

怎么理解ThreadPoolExecutor線程池技術(shù)

這個if判斷想要執(zhí)行到return false;,隊列為空是一個必要條件。因?yàn)閍ddWork()不單只接收新任務(wù)會調(diào)用到,處理隊列中的任務(wù)也會調(diào)用到。而前面提到SHUTDOWN狀態(tài)下還會處理隊列中的任務(wù)的,所以隊列不為空是會讓它繼續(xù)執(zhí)行下去的。

對于內(nèi)層的for循環(huán)

怎么理解ThreadPoolExecutor線程池技術(shù)

會先判斷worker的數(shù)據(jù)是否符合corePoolSize和maximumPoolSize的定義,不滿足則返回失敗。 然后嘗試CAS讓workerCount自增,如果CAS失敗還是繼續(xù)自旋去自增,直到成功。除非線程池狀態(tài)發(fā)生了變化,發(fā)退回到外層for循環(huán)重新執(zhí)行,判斷線程池的狀態(tài)。

第一段的代碼,就是讓workerCount在符合條件下自增

第二段代碼

怎么理解ThreadPoolExecutor線程池技術(shù)

這段比較好理解,先創(chuàng)建一個Worker對象,這個Worker里面包含一個由線程工廠創(chuàng)建的線程,和一個需要執(zhí)行的任務(wù)(可以為空)。如果線程創(chuàng)建成功了,那么就加一個重入鎖去把這個新建的Worker對象放到workers成員變量中,在加入之前需要重新判斷下線程池的狀態(tài)和新建線程的狀態(tài)。如果worker添加到workers成員變量中,就啟動這個新建的線程。最后如果添加失敗,則執(zhí)行addWorkFailed(w)

怎么理解ThreadPoolExecutor線程池技術(shù)

如果失敗了,加鎖操作回滾下wokers、workerCount,然后判斷下狀態(tài)看看是否需要終結(jié)線程池。

addWorker()大概的流程就這樣。

對于其他方法,沒有什么特別的,在此不再過多的敘述,有興趣的可以翻翻源碼閱讀下。 回顧總結(jié)下上面的核心要點(diǎn)

  1. 當(dāng)核心線程滿且忙碌時,線程池傾向于把提交的任務(wù)放進(jìn)隊列,而不是新建線程。

  2. 根據(jù)選擇隊列的不同,maximumPoolSize不一定有用的。具體有三種不同的策略。

  3. ctl是線程池的核心控制狀態(tài),包含的runState線程池運(yùn)行狀態(tài)和workCount有效線程數(shù)。

  4. retry:是一種標(biāo)記循環(huán)的語法,retry可以是任何變量命名合法字符。

上述內(nèi)容就是怎么理解ThreadPoolExecutor線程池技術(shù),你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI