欧美一区二区三区,国内熟女精品熟女A片视频小说,日本av网,小鲜肉男男GAY做受XXX网站

如何更好的使用JAVA線程池?

錢淋西2年前12瀏覽0評論

線程池是為了解決線程創(chuàng)建資源消耗問題而出現(xiàn)的。所以要更好的使用線程池就要分別從線程池大小參數(shù)的設(shè)置、工作線程的創(chuàng)建、空閑線程的回收、阻塞隊(duì)列的使用、任務(wù)拒絕策略、線程池Hook等方面來了解線程池的使用,其中涉及到一些細(xì)節(jié)包括不同參數(shù)、不同隊(duì)列、不同拒絕策略的選擇、產(chǎn)生的影響和行為、為更好的使用線程池奠定知識基礎(chǔ)。

ExecutorService基于池化的線程來執(zhí)行用戶提交的任務(wù),通常可以簡單的通過Executors提供的工廠方法來創(chuàng)建ThreadPoolExecutor實(shí)例。

線程池解決的兩個問題:

  1. 線程池通過減少每次做任務(wù)的時候產(chǎn)生的性能消耗來優(yōu)化執(zhí)行大量的異步任務(wù)的時候的系統(tǒng)性能。
  2. 線程池還提供了限制和管理批量任務(wù)被執(zhí)行的時候消耗的資源、線程的方法。另外ThreadPoolExecutor還提供了簡單的統(tǒng)計功能,比如當(dāng)前有多少任務(wù)被執(zhí)行完了。


快速開始

為了使得線程池適合大量不同的應(yīng)用上下文環(huán)境,ThreadPoolExecutor提供了很多可以配置的參數(shù)和可被用來擴(kuò)展的鉤子。然而,用戶還可以通過使用Executors提供的一些工廠方法來快速創(chuàng)建ThreadPoolExecutor實(shí)例。比如:

  1. 使用Executors#newCachedThreadPool可以快速創(chuàng)建一個擁有自動回收線程功能且沒有限制的線程池。
  2. 使用Executors#newFixedThreadPool可以用來創(chuàng)建一個固定線程大小的線程池。
  3. 使用Executors#newSingleThreadExecutor可以用來創(chuàng)建一個單線程的執(zhí)行器。

如果上面的方法創(chuàng)建的實(shí)例不能滿足我們的需求,我們可以自己通過參數(shù)來配置,實(shí)例化一個實(shí)例。

關(guān)于線程數(shù)大小參數(shù)設(shè)置

ThreadPoolExecutor會根據(jù)corePoolSize和maximumPoolSize來動態(tài)調(diào)整線程池的大小:poolSize。

當(dāng)任務(wù)通過executor提交給線程池的時候,我們需要知道下面幾個點(diǎn):

  • 如果這個時候當(dāng)前池子中的工作線程數(shù)小于corePoolSize,則新創(chuàng)建一個新的工作線程來執(zhí)行這個任務(wù),不管工作線程集合中有沒有線程是處于空閑狀態(tài)。
  • 如果池子中有比corePoolSize大的但是比maximumPoolSize小的工作線程,任務(wù)會首先被嘗試著放入隊(duì)列,這里有兩種情況需要單獨(dú)說一下:
  1. 如果任務(wù)被成功的放入隊(duì)列,則看看是否需要開啟新的線程來執(zhí)行任務(wù),只有當(dāng)當(dāng)前工作線程數(shù)為0的時候才會創(chuàng)建新的線程,因?yàn)橹暗木€程有可能因?yàn)槎继幱诳臻e狀態(tài)或因?yàn)楣ぷ鹘Y(jié)束而被移除。
  2. 如果放入隊(duì)列失敗,則才會去創(chuàng)建新的工作線程。
  • 如果corePoolSize和maximumPoolSize相同,則線程池的大小是固定的。
  • 通過將maximumPoolSize設(shè)置為無限大,我們可以得到一個無上限的線程池。
  • 除了通過構(gòu)造參數(shù)設(shè)置這幾個線程池參數(shù)之外我們還可以在運(yùn)行時設(shè)置。


核心線程WarmUp

默認(rèn)情況下,核心工作線程值在初始的時候被創(chuàng)建,當(dāng)新任務(wù)來到的時候被啟動,但是我們可以通過重寫prestartCoreThread或prestartCoreThreads方法來改變這種行為。通常場景我們可以在應(yīng)用啟動的時候來WarmUp核心線程,從而達(dá)到任務(wù)過來能夠立馬執(zhí)行的結(jié)果,使得初始任務(wù)處理的時間得到一定優(yōu)化。


定制工作線程的創(chuàng)建

新的線程是通過ThreadFactory來創(chuàng)建的,如果沒有指定,默認(rèn)的Executors#defaultThreadFactory將被使用,這個時候創(chuàng)建的線程將都屬于同一個線程組,擁有同樣的優(yōu)先級和daemon狀態(tài)。擴(kuò)展配置ThreadFactory,我們可以配置線程的名字、線程組合daemon狀態(tài)。如果調(diào)用ThreadFactory#createThread的時候失敗,將返回null,executor將不會執(zhí)行任何任務(wù)。


空閑線程回收

如果當(dāng)前池子中的工作線程數(shù)大于corePoolSize,如果超過這個數(shù)字的線程處于空閑的時間大于keepAliveTime,則這些線程將會被終止,這是一種減少不必要資源消耗的策略。這個參數(shù)可以在運(yùn)行時被改變,我們同樣可以將這種策略應(yīng)用給核心線程,我們可以通過調(diào)用allowCoreThreadTimeout來實(shí)現(xiàn)。


選擇合適的阻塞隊(duì)列

所有的阻塞隊(duì)列都可以被用來存放任務(wù),但是使用不同的隊(duì)列針對corePoolSize會表現(xiàn)不同的行為:

1、當(dāng)池中工作線程數(shù)小于corePoolSize的時候,每次來任務(wù)的時候都會創(chuàng)建一個新的工作線程。

2、當(dāng)池中工作線程數(shù)大于等于corePoolSize的時候,每次任務(wù)來的時候都會首先嘗試將線程放入隊(duì)列,而不是直接去創(chuàng)建線程。

3、如果放入隊(duì)列失敗,且當(dāng)先池中線程數(shù)小于maximumPoolSize的時候,則會創(chuàng)建一個工作線程。

下面主要是不同隊(duì)列策略表現(xiàn):

1、直接遞交:一種比較好的默認(rèn)選擇是使用SynchronousQueue,這種策略會將提交的任務(wù)直接傳送給工作線程,而不持有。如果當(dāng)前沒有工作線程來處理,即任務(wù)放入隊(duì)列失敗,則根據(jù)線程池的實(shí)現(xiàn),會引發(fā)新的工作線程創(chuàng)建,因此新提交的任務(wù)會被處理。這種策略在當(dāng)提交的一批任務(wù)之間有依賴關(guān)系的時候避免了鎖競爭消耗。值得一提的是,這種策略最好是配合unbounded線程數(shù)來使用,從而避免任務(wù)被拒絕。同時我們必須要考慮到一種場景,當(dāng)任務(wù)到來的速度大于任務(wù)處理的速度,將會引起無限制的線程數(shù)不斷的增加。

2、無界隊(duì)列:使用無界隊(duì)列如LinkedBlockingQueue沒有指定最大容量的時候,將會引起當(dāng)核心線程都在忙的時候,新的任務(wù)被放在隊(duì)列上,因此,永遠(yuǎn)不會有大于corePoolSize的線程被創(chuàng)建,因此maximumPoolSize參數(shù)將失效。這種策略比較適合所有的任務(wù)都不相互依賴,獨(dú)立執(zhí)行。舉個例子,如網(wǎng)頁服務(wù)器中,每個線程獨(dú)立處理請求。但是當(dāng)任務(wù)處理速度小于任務(wù)進(jìn)入速度的時候會引起隊(duì)列的無限膨脹。

3、有界隊(duì)列:有界隊(duì)列如ArrayBlockingQueue幫助限制資源的消耗,但是不容易控制。隊(duì)列長度和maximumPoolSize這兩個值會相互影響,使用大的隊(duì)列和小maximumPoolSize會減少CPU的使用、操作系統(tǒng)資源、上下文切換的消耗,但是會降低吞吐量,如果任務(wù)被頻繁的阻塞如IO線程,系統(tǒng)其實(shí)可以調(diào)度更多的線程。使用小的隊(duì)列通常需要大maximumPoolSize,從而使得CPU更忙一些,但是又會增加降低吞吐量的線程調(diào)度的消耗。總結(jié)一下是IO密集型可以考慮多些線程來平衡CPU的使用,CPU密集型可以考慮少些線程減少線程調(diào)度的消耗。


選擇適合的拒絕策略

當(dāng)新的任務(wù)到來的而線程池被關(guān)閉的時候,或線程數(shù)和隊(duì)列已經(jīng)達(dá)到上限的時候,我們需要去做一個決定,怎么拒絕這些任務(wù)。下面介紹一下常用的策略:

1、ThreadPoolExecutor#AbortPolicy:這個策略直接拋出RejectedExecutionException異常。

2、ThreadPoolExecutor#CallerRunsPolicy:這個策略將會使用Caller線程來執(zhí)行這個任務(wù),這是一種feedback策略,可以降低任務(wù)提交的速度。

3、ThreadPoolExecutor#DiscardPolicy:這個策略將會直接丟棄任務(wù)。

4、ThreadPoolExecutor#DiscardOldestPolicy:這個策略將會把任務(wù)隊(duì)列頭部的任務(wù)丟棄,然后重新嘗試執(zhí)行,如果還是失敗則繼續(xù)實(shí)施策略。

除了上面的幾種策略,我們也可以通過實(shí)現(xiàn)RejectedExecutionHandler來實(shí)現(xiàn)自己的策略。


利用Hook嵌入你的行為

ThreadPoolExecutor提供了protected類型可以被覆蓋的鉤子方法,允許用戶在任務(wù)執(zhí)行之前會執(zhí)行之后做一些事情。我們可以通過它來實(shí)現(xiàn)比如初始化ThreadLocal、收集統(tǒng)計信息、如記錄日志等操作。這類Hook如beforeExecute和afterExecute。另外還有一個Hook可以用來在任務(wù)被執(zhí)行完的時候讓用戶插入邏輯,如rerminated。

如果hook方法執(zhí)行失敗,則內(nèi)部的工作線程的執(zhí)行將會失敗或被中斷。


可訪問的隊(duì)列

getQueue方法可以用來訪問queue隊(duì)列以進(jìn)行一些統(tǒng)計或者debug工作,我們不建議用作其他用途。同時remove方法和purge方法可以用來將任務(wù)從隊(duì)列中移除。


關(guān)閉線程池

當(dāng)線程池不在被引用并且工作線程數(shù)為0的時候,線程池將被終止。我們也可以調(diào)用shutdown來手動終止線程池。如果我們忘記調(diào)用shutdown,為了讓線程資源被釋放,我們還可以使用keepAliveTime和allowCoreThreadTimeOut來達(dá)到目的。

我們在寫程序時要注意自己的應(yīng)用場景,根據(jù)場景使用合適的策略和參數(shù)才是正確的打開方式,有問題可以私信我。歡迎交流!