Java中線程池怎么創(chuàng)建 詳解Java四種線程池的適用場景

java線程池通過預(yù)先創(chuàng)建線程提升并發(fā)效率,避免頻繁創(chuàng)建銷毀開銷。1. 使用executors工廠類可快速創(chuàng)建預(yù)定義線程池:newfixedthreadpool適用于任務(wù)量穩(wěn)定的場景;newcachedthreadpool適合任務(wù)量不確定但執(zhí)行時間短的場景;newsinglethreadexecutor用于順序執(zhí)行任務(wù);newscheduledthreadpool適合周期性任務(wù)。2. 使用threadpoolexecutor可精細(xì)配置:需設(shè)置corepoolsize、maximumpoolsize、keepalivetime、workqueue及handler等參數(shù)。3. 線程池大小應(yīng)根據(jù)cpu核心數(shù)、任務(wù)類型和io占比合理設(shè)置,常用公式為線程數(shù)=cpu核心數(shù)*(1+io耗時占比)。4. 異常處理可通過任務(wù)內(nèi)部捕獲、future檢查或自定義uncaughtexceptionhandler實現(xiàn)。5. 通過getpoolsize()、getactivecount()等方法監(jiān)控線程池狀態(tài)以進(jìn)行調(diào)優(yōu)。

Java中線程池怎么創(chuàng)建 詳解Java四種線程池的適用場景

Java線程池的創(chuàng)建,本質(zhì)上是在資源可控的前提下,提升并發(fā)處理效率。簡單來說,就是預(yù)先創(chuàng)建一些線程,任務(wù)來了直接用,用完放回去,避免頻繁創(chuàng)建銷毀線程的開銷。

Java中線程池怎么創(chuàng)建 詳解Java四種線程池的適用場景

解決方案

Java中線程池怎么創(chuàng)建 詳解Java四種線程池的適用場景

Java提供了ExecutorService接口和ThreadPoolExecutor類來實現(xiàn)線程池。最常用的方式是通過Executors工廠類來創(chuàng)建幾種預(yù)定義的線程池,當(dāng)然,也可以直接使用ThreadPoolExecutor進(jìn)行更精細(xì)的配置。

立即學(xué)習(xí)Java免費學(xué)習(xí)筆記(深入)”;

Java中線程池怎么創(chuàng)建 詳解Java四種線程池的適用場景

  1. 使用Executors工廠類:

    Executors提供了以下幾種常用的線程池:

    • newFixedThreadPool(int nThreads):創(chuàng)建一個固定大小的線程池,核心線程數(shù)和最大線程數(shù)相等,沒有超時機(jī)制。適合任務(wù)量穩(wěn)定,需要快速響應(yīng)的場景。
    • newCachedThreadPool():創(chuàng)建一個可緩存的線程池,線程數(shù)可以無限擴(kuò)展,當(dāng)線程空閑超過60秒時會被回收。適合任務(wù)量不確定,但每個任務(wù)執(zhí)行時間較短的場景。
    • newSingleThreadExecutor():創(chuàng)建一個單線程的線程池,所有任務(wù)按照FIFO的順序執(zhí)行。適合需要保證任務(wù)順序執(zhí)行的場景。
    • newScheduledThreadPool(int corePoolSize):創(chuàng)建一個可以執(zhí)行定時任務(wù)的線程池。適合需要周期性執(zhí)行任務(wù)的場景。

    示例:

    ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) {     executor.execute(new Task(i)); } executor.shutdown(); // 任務(wù)提交完畢后,關(guān)閉線程池

    這里創(chuàng)建了一個固定大小為10的線程池,然后提交了100個任務(wù)。executor.shutdown()用于平滑關(guān)閉線程池,不再接受新的任務(wù),但會等待已提交的任務(wù)執(zhí)行完成。如果不調(diào)用shutdown(),程序可能不會正常退出。

  2. 使用ThreadPoolExecutor類:

    ThreadPoolExecutor提供了更靈活的配置選項,可以根據(jù)實際需求進(jìn)行定制。

    ThreadPoolExecutor executor = new ThreadPoolExecutor(     5, // corePoolSize:核心線程數(shù)     10, // maximumPoolSize:最大線程數(shù)     60L, // keepAliveTime:線程空閑時間     TimeUnit.SECONDS, // unit:時間單位     new LinkedBlockingQueue<Runnable>(100), // workQueue:任務(wù)隊列     new ThreadPoolExecutor.CallerRunsPolicy() // handler:拒絕策略 );
    • corePoolSize:核心線程數(shù),即使沒有任務(wù)也會保持存活的線程數(shù)。
    • maximumPoolSize:最大線程數(shù),當(dāng)任務(wù)隊列滿了之后,會創(chuàng)建新的線程來執(zhí)行任務(wù),直到達(dá)到最大線程數(shù)。
    • keepAliveTime:線程空閑時間,當(dāng)線程空閑時間超過這個值時,會被回收。
    • unit:時間單位,例如TimeUnit.SECONDS。
    • workQueue:任務(wù)隊列,用于存放等待執(zhí)行的任務(wù)。常用的隊列有LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue等。
    • handler:拒絕策略,當(dāng)任務(wù)隊列滿了且線程數(shù)達(dá)到最大線程數(shù)時,會執(zhí)行拒絕策略。常用的策略有AbortPolicy(拋出異常)、CallerRunsPolicy(由調(diào)用線程執(zhí)行任務(wù))、DiscardPolicy(丟棄任務(wù))、DiscardOldestPolicy(丟棄隊列中最老的任務(wù))。

    選擇合適的參數(shù)非常重要,例如,如果任務(wù)隊列設(shè)置過小,容易導(dǎo)致拒絕策略被頻繁觸發(fā);如果最大線程數(shù)設(shè)置過大,可能會消耗過多的系統(tǒng)資源。

四種線程池的適用場景

  • FixedThreadPool: 適合處理CPU密集型任務(wù),或者對響應(yīng)時間有嚴(yán)格要求的場景。因為線程數(shù)固定,可以避免頻繁的線程創(chuàng)建和銷毀開銷。想象一下,一個服務(wù)器需要快速響應(yīng)客戶端的請求,就可以使用FixedThreadPool。

  • CachedThreadPool: 適合處理大量的、耗時短的任務(wù)。線程可以動態(tài)擴(kuò)展,避免了線程阻塞,提高了吞吐量。例如,處理大量的http請求,每個請求的處理時間都很短。

  • SingleThreadExecutor: 適合需要保證任務(wù)順序執(zhí)行的場景,例如,處理消息隊列中的消息,需要按照消息的順序進(jìn)行處理。

  • ScheduledThreadPool: 適合需要周期性執(zhí)行任務(wù)的場景,例如,定時備份數(shù)據(jù)庫,或者定時發(fā)送心跳包。

如何選擇合適的線程池大小?

選擇合適的線程池大小,需要綜合考慮CPU核心數(shù)、任務(wù)類型、IO密集程度等因素。一個常用的公式是:

線程數(shù) = CPU核心數(shù) * (1 + IO耗時占比)

如果任務(wù)是CPU密集型的,IO耗時占比接近于0,那么線程數(shù)可以設(shè)置為CPU核心數(shù)。如果任務(wù)是IO密集型的,IO耗時占比很高,那么線程數(shù)可以設(shè)置得比CPU核心數(shù)大一些。

線程池的異常處理?

在線程池中執(zhí)行任務(wù)時,如果任務(wù)拋出異常,默認(rèn)情況下,線程池會忽略這個異常,導(dǎo)致任務(wù)執(zhí)行失敗。為了更好地處理異常,可以使用以下幾種方式:

  • 在任務(wù)內(nèi)部捕獲異常,并進(jìn)行處理。
  • 使用Future來獲取任務(wù)的執(zhí)行結(jié)果,并檢查是否拋出異常。
  • 自定義ThreadFactory,在創(chuàng)建線程時設(shè)置UncaughtExceptionHandler,用于捕獲未捕獲的異常。

線程池的監(jiān)控和調(diào)優(yōu)?

可以使用ThreadPoolExecutor提供的方法來監(jiān)控線程池的狀態(tài),例如:

  • getPoolSize():獲取線程池中的線程數(shù)。
  • getActiveCount():獲取正在執(zhí)行任務(wù)的線程數(shù)。
  • getQueue().size():獲取任務(wù)隊列中的任務(wù)數(shù)。
  • getCompletedTaskCount():獲取已完成的任務(wù)數(shù)。

通過監(jiān)控這些指標(biāo),可以了解線程池的運行狀態(tài),并根據(jù)實際情況進(jìn)行調(diào)優(yōu),例如,調(diào)整線程池的大小、任務(wù)隊列的大小、拒絕策略等。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊15 分享