日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JUC系列(九)| ThreadPool 线程池

發(fā)布時(shí)間:2025/3/19 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JUC系列(九)| ThreadPool 线程池 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

多線程一直Java開發(fā)中的難點(diǎn),也是面試中的常客,趁著還有時(shí)間,打算鞏固一下JUC方面知識,我想機(jī)會隨處可見,但始終都是留給有準(zhǔn)備的人的,希望我們都能加油!!!

沉下去,再浮上來,我想我們會變的不一樣的。

🍟線程池介紹

1)什么是線程池?

線程池(英語:thread pool):一種線程使用模式。由系統(tǒng)維護(hù)的容納線程的容器,由CLR控制的所有AppDomain共享。線程池可用于執(zhí)行任務(wù)、發(fā)送工作項(xiàng)、處理異步 I/O、代表其他線程等待以及處理計(jì)時(shí)器。

2)為什么要使用線程池?

痛點(diǎn):

  • 不使用線程池的話,每次請求都會創(chuàng)建新的線程,然后銷毀,資源消耗大,復(fù)用率低。
  • 在Java中,線程的線程棧所占用的內(nèi)存在Java堆外,不受Java程序控制,只受系統(tǒng)資源限制(如若系統(tǒng)給資源不足,可能創(chuàng)建失敗),默認(rèn)一個(gè)線程的線程棧大小是1M。如果每個(gè)請求都新建線程,1024個(gè)線程就會占用1個(gè)G內(nèi)存,系統(tǒng)很容易崩潰。
  • 如若系統(tǒng)龐大,仍每次都在代碼中新建線程,不好管理,難以找到錯(cuò)誤,也不好監(jiān)控、調(diào)優(yōu)。
  • 線程過多會帶來調(diào)度開銷, 進(jìn)而影響緩存局部性和整體性能。
  • 原因:

    線程池維護(hù)著多個(gè)線程,等待著監(jiān)督管理者分配可并發(fā)執(zhí)行的任務(wù)。這避免了在處理短時(shí)間任務(wù)時(shí)創(chuàng)建與銷毀線程的代價(jià)。線程池不僅能夠保證內(nèi)核的充分利用,還能防止過分調(diào)度。

    線程池的優(yōu)勢

    線程池做的工作只要是控制運(yùn)行的線程數(shù)量,處理過程中將任務(wù)放入隊(duì)列,然后在線程創(chuàng)建后啟動這些任務(wù),如果線程數(shù)量超過了最大數(shù)量, 超出數(shù)量的線程排隊(duì)等候,等其他線程執(zhí)行完畢,再從隊(duì)列中取出任務(wù)來執(zhí)行。

    另外在我們常常看的阿里巴巴Java開發(fā)手冊也有提到:

    【強(qiáng)制】線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。

    說明:使用線程池的好處是減少在創(chuàng)建和銷毀線程上所花的時(shí)間以及系統(tǒng)資源的開銷,解決資源不足的問題。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者“過度切換”的問題。

    3)特點(diǎn)

    降低資源消耗:通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的銷耗。

    提高響應(yīng)速度:當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行。

    提高線程的可管理性: 線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。

    4)架構(gòu)

    Java 中的線程池是通過 Executor 框架實(shí)現(xiàn)的,該框架中用到了 Executor, ExecutorService,ThreadPoolExecutor, 這幾個(gè)類

    另外還有Executors這個(gè)方便封裝的工具類。但是不建議使用😂

    5)線程池參數(shù)說明

    ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
  • corePoolSize 線程池的核心線程數(shù)
    • 當(dāng)提交一個(gè)任務(wù)時(shí),如果當(dāng)前核心線程池的線程個(gè)數(shù)沒有達(dá)到 corePoolSize,則會創(chuàng)建新的線程來執(zhí)行所提交的任務(wù),即使當(dāng)前核心線程池有空閑的線程。如果當(dāng)前核心線程池的線程個(gè)數(shù)已經(jīng)達(dá)到了 corePoolSize,則不再重新創(chuàng)建線程。
  • maximumPoolSize 能容納的最大線程數(shù)
    • 即當(dāng)阻塞隊(duì)列已滿時(shí),并且當(dāng)前線程池線程個(gè)數(shù)沒有超過 maximumPoolSize 的話,就會創(chuàng)建新的線程來執(zhí)行任務(wù)。
  • keepAliveTime 空閑線程存活時(shí)間
    • 如果當(dāng)前線程池的線程個(gè)數(shù)已經(jīng)超過了 corePoolSize,并且線程空閑時(shí)間超過了 keepAliveTime 的話,就會將這些空閑線程銷毀,這樣可以盡可能降低系統(tǒng)資源消耗。
  • unit 存活的時(shí)間單位
  • workQueue 存放提交但未執(zhí)行任務(wù)的隊(duì)列,即阻塞隊(duì)列
  • threadFactory 創(chuàng)建線程的工廠類
  • handler 等待隊(duì)列滿后的拒絕策略

  • 線程池中,有三個(gè)重要的參數(shù),決定影響了拒絕策略:

  • corePoolSize - 核心線 程數(shù),也即最小的線程數(shù)。
  • workQueue - 阻塞隊(duì)列 。
  • maximumPoolSize - 最大線程數(shù) 當(dāng)提交任務(wù)數(shù)大于 corePoolSize 的時(shí)候,會優(yōu)先將任務(wù)放到 workQueue 阻塞隊(duì)列中。當(dāng)阻塞隊(duì)列飽和后,會擴(kuò)充線程池中線程數(shù),直到達(dá)到 maximumPoolSize 最大線程數(shù)配置。此時(shí),再多余的任務(wù),則會觸發(fā)線程池 的拒絕策略了。 總結(jié)起來,也就是一句話,當(dāng)提交的任務(wù)數(shù)大于(workQueue.size() + maximumPoolSize ),就會觸發(fā)線程池的拒絕策略。
  • 6)拒絕策略

  • AbortPolicy: 丟棄任務(wù),并拋出拒絕執(zhí)行 RejectedExecutionException 異常信息。線程池默認(rèn)的拒絕策略。必須處理好拋出的異常,否則會打斷當(dāng)前的執(zhí)行流程,影響后續(xù)的任務(wù)執(zhí)行。

  • CallerRunsPolicy: 當(dāng)觸發(fā)拒絕策略,只要線程池沒有關(guān)閉的話,則使用調(diào)用線程直接運(yùn)行任務(wù)。一般并發(fā)比較小,性能要求不高,不允許失敗。但是,由 于調(diào)用者自己運(yùn)行任務(wù),如果任務(wù)提交速度過快,可能導(dǎo)致程序阻塞,性能效 率上必然的損失較大

  • DiscardPolicy: 直接丟棄.

  • DiscardOldestPolicy: 觸發(fā)拒絕策略時(shí),只要線程池沒有關(guān)閉的話,丟棄阻塞隊(duì)列 workQueue中最老的任務(wù),并將新任務(wù)加入

  • 🧇線程池種類

    Jav有4種默認(rèn)線程池,分別是

    2.1、 newCachedThreadPool

    作用:創(chuàng)建一個(gè)可緩存線程池,此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程.

    比較適合用于創(chuàng)建一個(gè)可無限擴(kuò)大的線程池,執(zhí)行時(shí)間較短,任務(wù)多的場景。

    特點(diǎn)

  • 線程池中數(shù)量不固定,可以達(dá)到(Interger. MAX_VALUE)
  • 線程池中的線程可進(jìn)行緩存重復(fù)利用和回收(回收默認(rèn)時(shí)間為 1 分鐘,即1分鐘內(nèi)沒有任務(wù)的線程)。
  • 創(chuàng)建方式

    /*** 可緩存線程池* @return*/ public static ExecutorService newCachedThreadPool() {/*** corePoolSize 線程池的核心線程數(shù)* maximumPoolSize 能容納的最大線程數(shù)* keepAliveTime 空閑線程存活時(shí)間* unit 存活的時(shí)間單位* workQueue 存放提交但未執(zhí)行任務(wù)的隊(duì)列* threadFactory 創(chuàng)建線程的工廠類:可以省略* handler 等待隊(duì)列滿后的拒絕策略:可以省略*/return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); }

    2.2、newFixedThreadPool

    作用

    用來創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池,以共享的無界隊(duì)列(LinkedBlockingQueue)方式來運(yùn)行這些線程。

    適用于預(yù)測并發(fā)壓力的情況下,對線程數(shù)做出限制;或者對線程數(shù)有嚴(yán)格限制的場景。

    特點(diǎn)

  • 線程數(shù)固定可重用。
  • corePoolSize和maximunPoolSize設(shè)定值相等。
  • keepAliveTime為0,一旦有多余的空閑線程,就會被立即停止掉。
  • 阻塞隊(duì)列采用了LinkedBlockingQueue,它是一個(gè)無界隊(duì)列,因此永遠(yuǎn)不可能拒絕任務(wù),超出的任務(wù)都需在隊(duì)列中等待。
  • 創(chuàng)建方式

    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); }

    2.3、newSingleThreadExecutor

    作用

    創(chuàng)建一個(gè)使用單個(gè) worker 線程的 Executor,以無界隊(duì)列(LinkedBlockingQueue)方式來運(yùn)行該線程。超出的任務(wù)都將存儲在隊(duì)列中,等待執(zhí)行。

    適用于需要保證順序執(zhí)行各個(gè)任務(wù),并且在任意時(shí)間點(diǎn),不會同時(shí)有多個(gè)線程的場景

    特點(diǎn)

    線程池中最多執(zhí)行 1 個(gè)線程,之后提交的線程活動將會排在隊(duì)列中以此 執(zhí)行

    創(chuàng)建方式

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

    2.4、newScheduledThreadPool

    作用

    創(chuàng)建一個(gè) corePoolSize 為傳入?yún)?shù),最大線程數(shù)為(Integer.MAX_VALUE)的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。

    比較適用于需要多個(gè)后臺線程執(zhí)行周期任務(wù)的場景,某個(gè)時(shí)候要收集日志了,發(fā)送推送消息拉等等都很合適😂

    特點(diǎn)

    可定時(shí)或延遲執(zhí)行線程活動~~(我拿這個(gè)模擬過動態(tài)定時(shí),意思是給定個(gè)未來某個(gè)時(shí)間,然后到那個(gè)時(shí)間點(diǎn)就執(zhí)行)~~

    創(chuàng)建方式

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); }public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory) {super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue(), threadFactory); }public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler); }

    🍖入門案例

    火車站 3 個(gè)售票口, 10 個(gè)用戶買票

    import java.util.concurrent.*;/*** 入門案例*/ public class ThreadPoolDemo1 {/*** 火車站 3 個(gè)售票口, 10 個(gè)用戶買票** @param args*/public static void main(String[] args) {//定時(shí)線程次:線程數(shù)量為 3---窗口數(shù)為 3ExecutorService threadService = new ThreadPoolExecutor(3,3,60L,TimeUnit.SECONDS,new LinkedBlockingQueue<>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());try {//10 個(gè)人買票for (int i = 1; i <= 10; i++) {threadService.execute(() -> {try {System.out.println(Thread.currentThread().getName() + " 窗口,開始賣票");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 窗口買票結(jié)束");} catch (Exception e) {e.printStackTrace();}});}} catch (Exception e) {e.printStackTrace();} finally {//完成后結(jié)束threadService.shutdown();}} } /**pool-1-thread-3 窗口,開始賣票pool-1-thread-2 窗口,開始賣票pool-1-thread-1 窗口,開始賣票pool-1-thread-3 窗口買票結(jié)束pool-1-thread-2 窗口買票結(jié)束pool-1-thread-1 窗口買票結(jié)束....*/

    🍤線程池底層工作原理(重要)

    public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();//如果線程池的線程個(gè)數(shù)少于corePoolSize則創(chuàng)建新線程執(zhí)行當(dāng)前任務(wù)if (workerCountOf(c) < corePoolSize) {//執(zhí)行addworker,創(chuàng)建一個(gè)核心線程,創(chuàng)建失敗重新獲取ctlif (addWorker(command, true))return;c = ctl.get();} //如果工作線程數(shù)大于核心線程數(shù),判斷線程池的狀態(tài)是否為running,并且可以添加進(jìn)隊(duì)列 //如果線程池不是running狀態(tài),則執(zhí)行拒絕策略,(還是會調(diào)用一次addworker)if (isRunning(c) && workQueue.offer(command)) {//再次獲取ctl,進(jìn)行雙重檢索int recheck = ctl.get();//如果線程池是不是處于RUNNING的狀態(tài),那么就會將任務(wù)從隊(duì)列中移除, //如果移除失敗,則會判斷工作線程是否為0 ,如果過為0 就創(chuàng)建一個(gè)非核心線程 //如果移除成功,就執(zhí)行拒絕策略,因?yàn)榫€程池已經(jīng)不可用了;if (! isRunning(recheck) && remove(command))//為給定的命令調(diào)用被拒絕的執(zhí)行處理程序reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}//檢查是否可以根據(jù)當(dāng)前池狀態(tài)和給定界限(核心或最大值)添加新的工作線程。 //如果是這樣,則相應(yīng)地調(diào)整工作人員數(shù)量,并且如果可能,將創(chuàng)建并啟動一個(gè)新工作人員,將 firstTask 作為其第一個(gè)任務(wù)運(yùn)行。 //如果池已停止或有資格關(guān)閉,則此方法返回 false。 //如果線程工廠在詢問時(shí)未能創(chuàng)建線程,它也會返回 false。 //如果線程創(chuàng)建失敗,要么是由于線程工廠返回 null,要么是由于異常(通常是 Thread.start() 中的 OutOfMemoryError),我們會干凈利落地回滾。else if (!addWorker(command, false))// 為給定的命令調(diào)用被拒絕的執(zhí)行處理程序reject(command); }

    上圖取自:線程池ThreadPoolExecutor實(shí)現(xiàn)原理 作者:你聽___

    execute執(zhí)行邏輯

  • 在創(chuàng)建了線程池后,線程池中的線程數(shù)為零
  • 當(dāng)調(diào)用 execute()方法添加一個(gè)請求任務(wù)時(shí),線程池會做出如下判斷:
    • 如果正在運(yùn)行的線程數(shù)量小于 corePoolSize,那么馬上創(chuàng)建線程運(yùn)行這個(gè)任務(wù);
    • 如果正在運(yùn)行的線程數(shù)量大于或等于 corePoolSize,那么將這個(gè)任務(wù)放入 隊(duì)列;
    • 如果這個(gè)時(shí)候隊(duì)列滿了且正在運(yùn)行的線程數(shù)量還小于 maximumPoolSize,那么還是要?jiǎng)?chuàng)建非核心線程立刻運(yùn)行這個(gè)任務(wù);
    • 如果隊(duì)列滿了且正在運(yùn)行的線程數(shù)量大于或等于 maximumPoolSize,那么線程 池會啟動飽和拒絕策略來執(zhí)行。
  • 當(dāng)一個(gè)線程完成任務(wù)時(shí),它會從隊(duì)列中取下一個(gè)任務(wù)來執(zhí)行
  • 當(dāng)一個(gè)線程無事可做超過一定的時(shí)間(keepAliveTime)時(shí),線程會判斷:
  • 如果當(dāng)前運(yùn)行的線程數(shù)大于 corePoolSize,那么這個(gè)線程就被停掉。
  • 所以線程池的所有任務(wù)完成后,它最終會收縮到 corePoolSize 的大小。
  • 🍦注意事項(xiàng)

    創(chuàng)建線程池推薦適用 ThreadPoolExecutor 及其 7 個(gè)參數(shù)手動創(chuàng)建

    為什么不使用Executors創(chuàng)建?

    🍕自言自語

    最近又開始了JUC的學(xué)習(xí),感覺Java內(nèi)容真的很多,但是為了能夠走的更遠(yuǎn),還是覺得應(yīng)該需要打牢一下基礎(chǔ)。

    最近在持續(xù)更新中,如果你覺得對你有所幫助,也感興趣的話,關(guān)注我吧,讓我們一起學(xué)習(xí),一起討論吧。

    你好,我是博主寧在春,Java學(xué)習(xí)路上的一顆小小的種子,也希望有一天能扎根長成蒼天大樹。

    希望與君共勉😁

    我們:待別時(shí)相見時(shí),都已有所成

    參考:

    線程池ThreadPoolExecutor實(shí)現(xiàn)原理

    https://www.zhihu.com/question/23212914/answer/245992718 作者:大閑人柴毛毛

    阿里巴巴開發(fā)手冊

    總結(jié)

    以上是生活随笔為你收集整理的JUC系列(九)| ThreadPool 线程池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。