欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

簡(jiǎn)單剖析Java中動(dòng)態(tài)線程池的擴(kuò)容以及縮容操作

 更新時(shí)間:2025年01月24日 08:42:50   作者:程序員博博  
這篇文章主要為大家詳細(xì)介紹了Java中動(dòng)態(tài)線程池的擴(kuò)容以及縮容操作的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

在項(xiàng)目中,我們經(jīng)常會(huì)使用到線程來(lái)處理加快我們的任務(wù)。但為了節(jié)約資源,大多數(shù)程序員都會(huì)把線程進(jìn)行池化,使用線程池來(lái)更好的支持我們的業(yè)務(wù)。

Java線程池ThreadPoolExecutor有幾個(gè)比較核心的參數(shù),如corePoolSize、maximumPoolSize等等。無(wú)論是在工作中還是在面試中,都會(huì)被問(wèn)到,如何正確的設(shè)置這幾個(gè)參數(shù)。

線程池的參數(shù)并不好配置。一方面線程池的運(yùn)行機(jī)制不是很好理解,配置合理需要強(qiáng)依賴(lài)開(kāi)發(fā)人員的個(gè)人經(jīng)驗(yàn)和知識(shí)。項(xiàng)目IO密集型還是CPU密集型等等,總歸很難確定一個(gè)完美的參數(shù),此時(shí)就有了動(dòng)態(tài)線程池的誕生。

動(dòng)態(tài)線程池(DTP)原理

其實(shí)動(dòng)態(tài)線程池并不是很高大上的技術(shù),它底層依舊是依賴(lài)了ThreadPoolExecutor的一些核心接口方法。我們通過(guò)下面圖片可以很清楚的看到,ThreadPoolExecutor本身就給我們提供了很多鉤子方法,讓我們?nèi)ザㄖ苹?/p>

那么其原理也非常簡(jiǎn)單了,我們?cè)谶\(yùn)行中假設(shè)有一個(gè)線程池叫做TaskExecutor

  • 他的核心線程池默認(rèn)假設(shè)是10,現(xiàn)在我發(fā)覺(jué)不夠用了,此時(shí)我想把他的核心線程池調(diào)整為20
  • 我可以寫(xiě)一個(gè)遠(yuǎn)程配置(可以阿波羅,zk,redis什么都可以)。然后監(jiān)聽(tīng)到了這個(gè)配置變?yōu)榱薱ore.pool.size=20
  • 然后我獲取到了這個(gè)線程池TaskExecutor,并且調(diào)用setCorePoolSize(20),那么這個(gè)TaskExecutor核心線程數(shù)就變?yōu)榱?0

就是這么簡(jiǎn)單,撥開(kāi)表面,探究原理,內(nèi)部其實(shí)非常的簡(jiǎn)單。當(dāng)時(shí)公司里面的線程池還有加一些友好的界面、監(jiān)控告警、操作日志、權(quán)限校驗(yàn)、審核等等,但本質(zhì)就是監(jiān)聽(tīng)配置,然后調(diào)用setCorePoolSize方法去實(shí)現(xiàn)的,最大線程數(shù)類(lèi)似。

public void setCorePoolSize(int corePoolSize) {
    if (corePoolSize < 0)
        throw new IllegalArgumentException();
    int delta = corePoolSize - this.corePoolSize;
    this.corePoolSize = corePoolSize;
    if (workerCountOf(ctl.get()) > corePoolSize)
        interruptIdleWorkers();
    else if (delta > 0) {
        int k = Math.min(delta, workQueue.size());
        while (k-- > 0 && addWorker(null, true)) {
            if (workQueue.isEmpty())
                break;
        }
    }
}

動(dòng)態(tài)線程池縮容

首先提出幾個(gè)問(wèn)題

  • 核心線程數(shù)為5,現(xiàn)在有3個(gè)線程在執(zhí)行,并且沒(méi)有執(zhí)行完畢,我修改核心線程數(shù)為4,是否修改成功
  • 核心線程數(shù)為5,現(xiàn)在有3個(gè)線程在執(zhí)行,并且沒(méi)有執(zhí)行完畢,我修改核心線程數(shù)為1,是否修改成功

讓我們帶著疑問(wèn)去思考問(wèn)題。

  • 首先第一個(gè)問(wèn)題,因?yàn)楹诵木€程池?cái)?shù)為5,僅有3個(gè)在執(zhí)行,我修改為4,那么因?yàn)橛?個(gè)空閑的線程,它只需要銷(xiāo)毀1個(gè)空閑線程即可,因此是成功的
  • 第二個(gè)問(wèn)題,核心線程池?cái)?shù)為5,僅有3個(gè)在執(zhí)行,我修改為1。雖然有2個(gè)空閑線程,但是我需要銷(xiāo)毀4個(gè)線程。因?yàn)橛?個(gè)空閑線程,2個(gè)非空閑線程。我只能銷(xiāo)毀2個(gè)空閑線程,另外2個(gè)執(zhí)行的任務(wù)不能被打斷,也就是執(zhí)行后仍然為3個(gè)核心線程數(shù)。
  • 那什么時(shí)候銷(xiāo)毀剩下2個(gè)執(zhí)行的線程呢,等到2個(gè)執(zhí)行的任務(wù)完畢之后,就會(huì)銷(xiāo)毀它了。假設(shè)這個(gè)任務(wù)是一個(gè)死循環(huán),永遠(yuǎn)不會(huì)結(jié)束,那么核心線程數(shù)永遠(yuǎn)是3,永遠(yuǎn)不能設(shè)置為1

我們舉一個(gè)代碼的例子如下

ThreadPoolExecutor es = new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
es.prestartAllCoreThreads();  // 預(yù)啟動(dòng)所有核心線程

// 啟動(dòng)三個(gè)任務(wù),執(zhí)行次數(shù)不一樣
for (int i = 0; i < 3; i++) {
    int finalI = i;
    es.execute(() -> {
        int cnt = 0;
        while (true) {
            try {
                cnt++;
                TimeUnit.SECONDS.sleep(2);

                if (cnt > finalI + 1) {
                    log.info(Thread.currentThread().getName() + " 執(zhí)行完畢");
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}
TimeUnit.SECONDS.sleep(1);  // 等待線程池中的線程執(zhí)行
log.info("修改前 es = {}", es);  // 這里核心線程數(shù)必定是5

es.setCorePoolSize(1);  // 修改核心線程數(shù)為1,但是核心線程數(shù)為5,并且有3個(gè)線程在執(zhí)行任務(wù),

while (true) {
    TimeUnit.SECONDS.sleep(1); // 等待
    log.info("修改后 es = {}", es);
}

輸出結(jié)果為如下

// 修改前核心線程數(shù)為5,運(yùn)行線程數(shù)為3
[修改前 es = java.util.concurrent.ThreadPoolExecutor@72d818d1[Running, pool size = 5, active threads = 3, queued tasks = 0, completed tasks = 0]]

// 因?yàn)橛?個(gè)空閑線程,先把2個(gè)空閑線程給銷(xiāo)毀了,剩下3個(gè)線程
[修改后 es = java.util.concurrent.ThreadPoolExecutor@72d818d1[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]]

// 等第1個(gè)任務(wù)執(zhí)行完畢,剩下2個(gè)線程
[Main.lambda$d$0:38] [pool-2-thread-1 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@72d818d1[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 1]]

// 等第2個(gè)任務(wù)執(zhí)行完畢,剩下1個(gè)線程
[Main.lambda$d$0:38] [pool-2-thread-2 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@72d818d1[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 2]]

// 等第3個(gè)任務(wù)執(zhí)行完畢,剩下1個(gè)線程。因?yàn)槲倚薷牡木褪?個(gè)核心線程
[Main.lambda$d$0:38] [pool-2-thread-3 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@72d818d1[Running, pool size = 1, active threads = 0, queued tasks = 0, completed tasks = 3]]

有興趣的讀者可以拿這塊帶去自己去試試,輸出結(jié)果里面的注釋 我寫(xiě)的非常詳細(xì),大家可以詳細(xì)品品這塊輸出結(jié)果。

動(dòng)態(tài)線程池?cái)U(kuò)容

擴(kuò)容我就不提問(wèn)問(wèn)題了,和縮容異曲同工,但我希望讀者可以先看下以下代碼,不要看答案,認(rèn)為會(huì)輸出什么結(jié)果,看下是否和自己想的是否一樣,如果一樣,那說(shuō)明你已經(jīng)完全懂了,如果不一樣,是什么原因。

// 核心線程數(shù)1,最大線程數(shù)10
ThreadPoolExecutor es = new ThreadPoolExecutor(1, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
es.prestartAllCoreThreads();  // 預(yù)啟動(dòng)所有核心線程

for (int i = 0; i < 5; i++) {
    int finalI = i;
    es.execute(() -> {
        int cnt = 0;
        while (true) {
            try {
                cnt++;
                TimeUnit.SECONDS.sleep(2);

                if (cnt > finalI + 1) {
                    log.info(Thread.currentThread().getName() + " 執(zhí)行完畢");
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}

TimeUnit.SECONDS.sleep(1);  // 等待線程池中的線程執(zhí)行
log.info("修改前 es = {}", es);  // 這里核心線程數(shù)必定是1, 隊(duì)列里面有4個(gè)任務(wù)

es.setCorePoolSize(3);  // 修改核心線程數(shù)為3

while (true) {
    TimeUnit.SECONDS.sleep(1); // 等待
    log.info("修改后 es = {}", es);
}   

輸出結(jié)果為如下 (注意觀察輸出queued tasks的變化?。?!

[修改前 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 1, active threads = 1, queued tasks = 4, completed tasks = 0]]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]]

[Main.lambda$a$1:73] [pool-2-thread-1 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 3, active threads = 3, queued tasks = 1, completed tasks = 1]]

[Main.lambda$a$1:73] [pool-2-thread-2 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 2]]

[Main.lambda$a$1:73] [pool-2-thread-3 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 3]]

[Main.lambda$a$1:73] [pool-2-thread-1 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 3, active threads = 1, queued tasks = 0, completed tasks = 4]]

[Main.lambda$a$1:73] [pool-2-thread-2 執(zhí)行完畢]
[修改后 es = java.util.concurrent.ThreadPoolExecutor@1e397ed7[Running, pool size = 3, active threads = 0, queued tasks = 0, completed tasks = 5]]

最后

在業(yè)務(wù)中,我們?yōu)榱颂岣咝适褂昧司€程,為了加快線程我們使用了線程池,而又為了更好的利用線程池的資源,我們又實(shí)現(xiàn)了動(dòng)態(tài)化線程池。這也就是遇到問(wèn)題、探索問(wèn)題、解決問(wèn)題的一套思路吧。

我們從底層原理分析,發(fā)現(xiàn)動(dòng)態(tài)線程池的底層原理非常簡(jiǎn)單,希望大家不要恐懼,往往撥開(kāi)外衣,發(fā)現(xiàn)里面最根本的原理,才能是我們更好的捋清楚其中的邏輯。

到此這篇關(guān)于簡(jiǎn)單剖析Java中動(dòng)態(tài)線程池的擴(kuò)容以及縮容操作的文章就介紹到這了,更多相關(guān)Java動(dòng)態(tài)線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA提高第九篇 集合體系

    JAVA提高第九篇 集合體系

    這篇文章主要為大家詳細(xì)介紹了JAVA提高第九篇集合體系的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Java mybatis常見(jiàn)問(wèn)題及解決方案

    Java mybatis常見(jiàn)問(wèn)題及解決方案

    這篇文章主要介紹了Java mybatis常見(jiàn)問(wèn)題及解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 詳解Java8新特性如何防止空指針異常

    詳解Java8新特性如何防止空指針異常

    要說(shuō) Java 編程中哪個(gè)異常是你印象最深刻的,那 NullPointerException 空指針可以說(shuō)是臭名昭著的,不要說(shuō)初級(jí)程序員會(huì)碰到, 即使是中級(jí),專(zhuān)家級(jí)程序員稍不留神,就會(huì)掉入這個(gè)坑里,本文就和大家聊聊Java8新特性如何防止空指針異常
    2023-08-08
  • springboot+WebMagic+MyBatis爬蟲(chóng)框架的使用

    springboot+WebMagic+MyBatis爬蟲(chóng)框架的使用

    本文是對(duì)spring boot+WebMagic+MyBatis做了整合,使用WebMagic爬取數(shù)據(jù),然后通過(guò)MyBatis持久化爬取的數(shù)據(jù)到mysql數(shù)據(jù)庫(kù)。具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-08-08
  • JAVA中Object的常用方法

    JAVA中Object的常用方法

    JAVA中Object是所有對(duì)象的頂級(jí)父類(lèi),存在于java.lang包中,這個(gè)包不需要我們手動(dòng)導(dǎo)包,本文通過(guò)實(shí)例代碼介紹JAVA中Object的常用方法,感興趣的朋友一起看看吧
    2023-11-11
  • java微信開(kāi)發(fā)中的地圖定位功能

    java微信開(kāi)發(fā)中的地圖定位功能

    本文通過(guò)實(shí)例代碼給大家介紹了java微信開(kāi)發(fā)中的地圖定位功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-07-07
  • 關(guān)于JavaEE內(nèi)部類(lèi)的部分注意事項(xiàng)

    關(guān)于JavaEE內(nèi)部類(lèi)的部分注意事項(xiàng)

    這篇文章主要介紹了關(guān)于JavaEE內(nèi)部類(lèi)的部分注意事項(xiàng),將一個(gè)類(lèi)定義在另一個(gè)類(lèi)里面或者一個(gè)方法里面,這樣的類(lèi)稱(chēng)為內(nèi)部類(lèi),這是一種封裝思想,那么使用內(nèi)部類(lèi)的時(shí)候要注意些什么呢,讓我們一起來(lái)看看吧
    2023-03-03
  • Java switch關(guān)鍵字原理及用法詳解

    Java switch關(guān)鍵字原理及用法詳解

    這篇文章主要介紹了Java中 switch關(guān)鍵原理及用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java中的字符型文件流FileReader和FileWriter詳細(xì)解讀

    Java中的字符型文件流FileReader和FileWriter詳細(xì)解讀

    這篇文章主要介紹了Java中的字符型文件流FileReader和FileWriter詳細(xì)解讀,與字節(jié)型文件流不同,字節(jié)型文件流讀取和寫(xiě)入的都是一個(gè)又一個(gè)的字節(jié),而字符型文件流操作的單位是一個(gè)又一個(gè)的字符,字符型流認(rèn)為一個(gè)字母是一個(gè)字符,而一個(gè)漢字也是一個(gè)字符,需要的朋友可以參考下
    2023-10-10
  • Java ExecutorService四種線程池使用詳解

    Java ExecutorService四種線程池使用詳解

    這篇文章主要介紹了Java ExecutorService四種線程池使用詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02

最新評(píng)論