溫馨提示×

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

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

Java并發(fā)之怎么使用線程池

發(fā)布時(shí)間:2021-10-28 16:07:32 來(lái)源:億速云 閱讀:102 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“Java并發(fā)之怎么使用線程池”,在日常操作中,相信很多人在Java并發(fā)之怎么使用線程池問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Java并發(fā)之怎么使用線程池”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

線程池的作用

池化技術(shù)是一種很常見的計(jì)算機(jī)技術(shù),主要是為了復(fù)用和提高性能,如內(nèi)存池、連接池、對(duì)象池等。線程池也不例外,他的主要作用如下:

  • 提高性能:線程的頻繁創(chuàng)建和銷毀會(huì)產(chǎn)生的很大的系統(tǒng)開銷,線程池中的線程復(fù)用可以大幅度的減少這種不必要的開銷。

  • 復(fù)用和管理:方便對(duì)池子中的線程進(jìn)行管理和復(fù)用,避免在生產(chǎn)環(huán)境中大量的創(chuàng)建線程。

  • 解耦:只暴露提交任務(wù)的接口,將線程池的創(chuàng)建、銷毀等工作與業(yè)務(wù)解耦。

JDK 在并發(fā)包中為我們定義了一套 Executor 框架,幫助開發(fā)人員有效地進(jìn)行線程控制,有基礎(chǔ)的線程池類、有線程池工廠,但是最最重要還是  ThreaPoolExecutor,也是面試中最常問的知識(shí)點(diǎn)。本文重點(diǎn)介紹 ThreaPoolExecutor 的原理。

線程池的參數(shù)說(shuō)明

ThreaPoolExecutor(     int corePoolSize,     long keepAliveTime,     TimeUnit unit,     BlockingQueue<Runnable> workQueue,     ThreadFactory threadFactory,     RejectedExecutionHandler handler) )

ThreaPoolExecutor 參數(shù)的含義如下

  • corePoolSize: 線程池中核心線程的數(shù)量。

  • maximumPoolSize: 線程池中的最大線程數(shù)量。

  • keepAliveTime: 當(dāng)線程池?cái)?shù)量超過 corePoolSize 時(shí),多余的空閑線程的存活時(shí)間。即超過 coolPoolSize 的空閑線程在  多長(zhǎng)時(shí)間內(nèi),會(huì)被銷毀。

  • unit: keepAliveTime 的單位,可以為 時(shí)、分、秒等多種值。

  • workQueue: 任務(wù)隊(duì)列,存放被提交但尚未被執(zhí)行的任務(wù)。

  • threadFactory: 線程工廠,用于創(chuàng)建線程,一般用默認(rèn)即可。

  • handler: 拒絕策略,當(dāng)線程池處理不過來(lái)任務(wù)時(shí),如何拒絕任務(wù)。

以上參數(shù)中 workQueue、threadFactory、handler 相對(duì)復(fù)雜,需要單獨(dú)介紹,下面主要介紹下 ThreadFactory 和 RejectedExecutionHandler

1. 線程工廠:ThreadFactory

線程池中的線程都由 TrheadFactory 定義的線程工廠來(lái)創(chuàng)建,它是一個(gè)接口只有 Thread newThread(Runnable r)  方法,用來(lái)創(chuàng)建線程。雖然創(chuàng)建 ThreadPoolExecutor 的時(shí)候可以不指定該參數(shù),但是阿里巴巴編碼規(guī)約建議最好指定該參數(shù),有以下幾個(gè)好處:

  • 跟蹤線程池在何時(shí)、創(chuàng)建了多少線程。

  • 可以自定義線程池的名稱、組以及優(yōu)先級(jí)等信息。

  • 設(shè)置線程的其他狀態(tài)等,如守護(hù)進(jìn)程。

2. 拒絕策略:RejectedExecutionHandler

當(dāng)線程池線程數(shù)量達(dá)到 maxPoolSize 大小時(shí),再提交新的任務(wù)會(huì)執(zhí)行拒絕策略,JDK 定義了四種拒絕策略:

  • AbortPolicy 該策略直接拋出異常

  • CallerRunsPolicy  調(diào)用者線程處理任務(wù),該策略并不是真正的丟棄任務(wù),會(huì)讓當(dāng)前線程來(lái)執(zhí)行被拋棄的任務(wù),由于只有一個(gè)線程,所有的任務(wù)會(huì)被串行執(zhí)行。

  • DiscardOldestPolicy 丟棄最老的一個(gè)請(qǐng)求,即隊(duì)列頭部的即將被執(zhí)行的任務(wù),并嘗試再次提交當(dāng)前任務(wù)。

  • DiscardPolicy 該策略默默丟棄無(wú)法處理的任務(wù)。

以上四種拒絕策略都繼承了接口 RejectedExecutionHandler 并實(shí)現(xiàn)該接口的 rejectedExecution(Runnable r,  ThreadPoolExecutor executor) 方法。如果以上四種拒絕策略都滿足不了你的需求,可以自定義拒絕策略,繼承接口  RejectedExecutionHandler 并實(shí)現(xiàn)方法即可。

線程池的調(diào)度邏輯

ThreaPoolExecutor 對(duì)提交的任務(wù)處理邏輯如下圖,

Java并發(fā)之怎么使用線程池

1. 提交任務(wù)時(shí):

  • 如果線程池中的線程數(shù)小于 corePoolSize (無(wú)論是否有空閑線程),創(chuàng)建新的線程(謂之核心線程)來(lái)處理。

  • 如果線程池中的線程數(shù)已經(jīng)大于或者 corePoolSize ,新提交的任務(wù)將被放置到等候隊(duì)列中,等待調(diào)度。

  • 如果等待隊(duì)列已滿,并且線程池中的線程數(shù)量小于 maxPoolSize,將繼續(xù)創(chuàng)建新線程處理任務(wù)。

  • 如果隊(duì)列已滿且線程數(shù)量也達(dá)到了上限,將使用拒絕策略來(lái)處理。

2. 任務(wù)進(jìn)行中時(shí):

當(dāng)隊(duì)列中的任務(wù)已經(jīng)執(zhí)行完,部分線程開始空閑,非核心線程會(huì)在空閑后的 keepAliveTime 的時(shí)間內(nèi)自行銷毀。

而空閑核心線程是否退出取決于線程池的另一個(gè)參數(shù) allowCoreThreadTimeOut 。當(dāng)配置為 true  的時(shí)候,即使是核心線程,超時(shí)也會(huì)退出。

線程池的生命周期

線程池同線程一樣也有自己的生命周期,包括 RUNNING、SHUTDOWN、STOP、TIDYING 和 TERMINATED  五種狀態(tài),他們的轉(zhuǎn)換關(guān)系如下圖,并且這些轉(zhuǎn)換時(shí)不可逆的。

Java并發(fā)之怎么使用線程池

1. RUNNING

該狀態(tài)是線程池的工作狀態(tài),能夠接受新任務(wù)以及對(duì)接受的任務(wù)進(jìn)行處理。線程池的初始狀態(tài),即線程創(chuàng)建成功后就處理此狀態(tài)。

2. SHUTDOWN

關(guān)閉狀態(tài),線程池不再接受新的任務(wù),但是能繼續(xù)處理提交到線程池中的任務(wù)。線程狀態(tài) RUNNING 的情況下調(diào)用 shutdown()  方法進(jìn)入該狀態(tài)。

3. STOP

停止?fàn)顟B(tài),線程池不接受新的任務(wù),也不處理阻塞隊(duì)列中的任務(wù),同時(shí)會(huì)中斷正在執(zhí)行任務(wù)的線程。在線程處于 RUNNING 或者 SHUTDOWN 狀態(tài)下調(diào)用  shutdownNow() 方法進(jìn)入該狀態(tài)。

4. TIDYING

所有任務(wù)都銷毀了,workCount 為 0,會(huì)自動(dòng)從 RUNNING 或者 STOP 狀態(tài)轉(zhuǎn)化為 TIDYING 狀態(tài)。在轉(zhuǎn)換過程中會(huì)調(diào)用  terminated() 方法,ThreadPoolExecutor 類的 ternimated() 方法為空,如果想在線程池變成 TIDYING  的時(shí)候有所處理,可以重載該方法。

線程池在 SHUTDOWN 狀態(tài)下,阻塞隊(duì)列為空并且執(zhí)行任務(wù)為空時(shí)轉(zhuǎn)換為 TIDYING 狀態(tài);線程池在 STOP 狀態(tài)下,執(zhí)行的任務(wù)為空時(shí)轉(zhuǎn)換為  TIDYING 狀態(tài)。

5. TERMINATED

結(jié)束狀態(tài),線程池的最終狀態(tài),該狀態(tài)的線程池不會(huì)再有任何操作。線程池執(zhí)行 terminated() 方法后處于該狀態(tài)。

JDK 四種線程池

了解 ThreaPoolExecutor 的基本原理后再來(lái)看看 JDK 在 Executors 中為開發(fā)人員定義的四個(gè)線程池工廠方法,其實(shí)它們內(nèi)部調(diào)用的是  ThreaPoolExecutor,只是使用了不同的參數(shù),下面來(lái)了解下它們的特性。

newFixedThreadPool()  方法:該方法返回一個(gè)固定線程池?cái)?shù)量的線程,提交任務(wù)時(shí)如果線程池中有空閑線程,則立即執(zhí)行,沒有則新的任務(wù)會(huì)被緩存在一個(gè)任務(wù)隊(duì)列中,它創(chuàng)建線程池的代碼如下:

public static ExecutorService newFixedThreadPool(int nthread) {   return new ThreadPoolExecutor(nthread,                                  nthread,                                  0L,                                  TimeUnit.MILLSECCONDS,                                  new LikedBlockingQueue<Runnable>()) }

newSingleThreadExecutor()  方法:該方法返回只有一個(gè)線程的線程池,如果多余的任務(wù)提交到線程池,則被提交到任務(wù)隊(duì)列中。它創(chuàng)建線程池代碼如下:

public static ExecutorService newSingleThreadExecutor() {   return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,                                                                          1,                                                                         0L, TimeUnit.MILLISECONDS,                                                                         new LinkedBlockingQueue<Runnable>())); }

newCachedThreadPool() 方法:該方法返回一個(gè)可根據(jù)實(shí)際情況調(diào)整線程數(shù)的線程池,它的核心線程數(shù)為 0 ,線程總數(shù)為  Integer.MAX_VALUE ,隊(duì)列采用的是 SynchronousQueue,這樣即使線程滿,任務(wù)也不能提交到隊(duì)列中。

public static ExecutorService newCachedThreadPool() {         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                       60L, TimeUnit.SECONDS,                                       new SynchronousQueue<Runnable>());     }

newScheduledThreadPool():該方法一個(gè)固定長(zhǎng)度的線程池,并且以延遲或者定時(shí)的方式去執(zhí)行任務(wù)。它的隊(duì)列使用  DelayedWorkQueue,所以任務(wù)必須繼承 Delay 接口。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {     return new ScheduledThreadPoolExecutor(corePoolSize); }  public ScheduledThreadPoolExecutor(int corePoolSize) {     super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,           new DelayedWorkQueue()); }

到此,關(guān)于“Java并發(fā)之怎么使用線程池”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

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

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

AI