關(guān)于線程池你不得不知道的一些設(shè)置
看完我上一篇文章,「你都理解創(chuàng)建線程池的參數(shù)嗎?」之后,當(dāng)遇到這種問題,你覺得你完全能夠唬住面試官了,50k輕松到手。殊不知,要是面試官此刻給你來個(gè)反殺:
初始化線程池時(shí)可以預(yù)先創(chuàng)建線程嗎?線程池的核心線程可以被回收嗎?為什么?
如果此刻你一臉懵逼,這個(gè)要慌,問題很大,50k馬上變5k。
有細(xì)心的網(wǎng)友早就想到了這個(gè)問題:
在ThreadPoolExecutor線程池中,還有一些不常用的設(shè)置。我建議如果您在應(yīng)用場(chǎng)景中沒有特殊的要求,就不需要使用這些設(shè)置。
初始化線程池時(shí)可以預(yù)先創(chuàng)建線程嗎?
prestartAllCoreThreads
初始化線程池時(shí)是可以預(yù)先創(chuàng)建線程的,初始化線程池后,再調(diào)用prestartAllCoreThreads()方法,即可預(yù)先創(chuàng)建corePoolSize數(shù)量的核心線程,我們看源碼:
public int prestartAllCoreThreads() { int n = 0; while (addWorker(null, true)) ++n; return n; }
private boolean addWorker(Runnable firstTask, boolean core) { // .. }
addWorker方法目的是在線程池中添加任務(wù)并執(zhí)行,如果task為空,線程獲取任務(wù)執(zhí)行時(shí)調(diào)用getTask()方法,該方法從blockingQueue阻塞隊(duì)列中阻塞獲取任務(wù)執(zhí)行,因此線程不會(huì)釋放,留存在線程池中,如果core=true,說明任務(wù)只能利用核心線程來執(zhí)行。
所以該方法會(huì)在線程池總預(yù)先創(chuàng)建沒有任務(wù)執(zhí)行的線程,數(shù)量為corePoolSize。
下面我們測(cè)試一下:
從測(cè)試結(jié)果來看,線程池中已經(jīng)預(yù)先創(chuàng)建了corePoolSize數(shù)量的空閑線程。
prestartCoreThread
prestartCoreThread()同樣可以預(yù)先創(chuàng)建線程,只不過該方法只會(huì)與創(chuàng)建1條線程,我們來看源碼:
public boolean prestartCoreThread() { return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); }
從方法源碼可知,如果此時(shí)工作線程數(shù)量小于corePoolSize,那么就調(diào)用addWorker創(chuàng)建1條空閑核心線程。
下面我們測(cè)試一下:
從測(cè)試結(jié)果來看,線程池中已經(jīng)預(yù)先創(chuàng)建了1條空閑線程。
線程池的核心線程可以被回收嗎?
你可能會(huì)想到將corePoolSize的數(shù)量設(shè)置為0,從而線程池的所有線程都是“臨時(shí)”的,只有keepAliveTime存活時(shí)間,你的思路也許時(shí)正確的,但你有沒有想過一個(gè)很嚴(yán)重的后果,corePoolSize=0時(shí),任務(wù)需要填滿阻塞隊(duì)列才會(huì)創(chuàng)建線程來執(zhí)行任務(wù),阻塞隊(duì)列有設(shè)置長(zhǎng)度還好,如果隊(duì)列長(zhǎng)度無限大呢,你就等著OOM異常吧,所以用這種設(shè)置行為并不是我們所需要的。
有沒有什么設(shè)置可以回收核心線程呢?
allowCoreThreadTimeOut
ThreadPoolExecutor有一個(gè)私有成員變量:
private volatile boolean allowCoreThreadTimeOut;
如果allowCoreThreadTimeOut=true,核心線程在規(guī)定時(shí)間內(nèi)會(huì)被回收。
上面我也說了,當(dāng)線程空閑時(shí)會(huì)從blockingQueue阻塞隊(duì)列中阻塞獲取任務(wù)執(zhí)行,所以我們來看看是保證核心線程不被銷毀的,我們直接定位到源碼部位:
java.util.concurrent.ThreadPoolExecutor#getTask:
boolean timedOut = false; // Did the last poll() time out? for (;;) { // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } }
這里的關(guān)鍵值timed,如果allowCoreThreadTimeOut=true或者此時(shí)工作線程大于corePoolSize,timed=true,如果timed=true,會(huì)調(diào)用poll()方法從阻塞隊(duì)列中獲取任務(wù),否則調(diào)用take()方法獲取任務(wù)。
下面我來解釋這兩個(gè)方法:
- poll(long timeout, TimeUnit unit):從BlockingQueue取出一個(gè)任務(wù),如果不能立即取出,則可以等待timeout參數(shù)的時(shí)間,如果超過這個(gè)時(shí)間還不能取出任務(wù),則返回null;
- take():從blocking阻塞隊(duì)列取出一個(gè)任務(wù),如果BlockingQueue為空,阻斷進(jìn)入等待狀態(tài)直到BlockingQueue有新的任務(wù)被加入為止。
到這里,我們就很好地解釋了,當(dāng)allowCoreThreadTimeOut=true或者此時(shí)工作線程大于corePoolSize時(shí),線程調(diào)用BlockingQueue的poll方法獲取任務(wù),若超過keepAliveTime時(shí)間,則返回null,timedOut=true,則getTask會(huì)返回null,線程中的runWorker方法會(huì)退出while循環(huán),線程接下來會(huì)被回收。
下面我們測(cè)試一下:
以上所述是小編給大家介紹的java線程池設(shè)置詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
JAVA HTTP反向代理實(shí)現(xiàn)過程詳解
這篇文章主要介紹了JAVA HTTP反向代理實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06任何Bean通過實(shí)現(xiàn)ProxyableBeanAccessor接口即可獲得動(dòng)態(tài)靈活的獲取代理對(duì)象或原生對(duì)象的能力(最新推
這篇文章主要介紹了任何Bean通過實(shí)現(xiàn)ProxyableBeanAccessor接口即可獲得動(dòng)態(tài)靈活的獲取代理對(duì)象或原生對(duì)象的能力,通過示例代碼看到,借助ProxyableBeanAccessor接口默認(rèn)實(shí)現(xiàn)的getReal、getProxy、selfAs方法,很靈活的按需獲取代理或非代理對(duì)象,需要的朋友可以參考下2024-02-02idea運(yùn)行java的配置詳細(xì)教程(包含maven,mysql下載配置)
程序員們?cè)陂_發(fā)的時(shí)候,一定會(huì)用到Intellij?IDEA這個(gè)集成開發(fā)環(huán)境,這篇文章主要給大家介紹了關(guān)于idea運(yùn)行java的配置(包含maven,mysql下載配置)的相關(guān)資料,需要的朋友可以參考下2024-05-05使用vscode搭建javaweb項(xiàng)目的詳細(xì)步驟
我個(gè)人是很喜歡VsCode的,開源免費(fèi)、功能全面,所以為了方便,我把我?guī)缀跛械倪\(yùn)行都集成到了VsCode上來,JavaWeb也不例外,下面這篇文章主要給大家介紹了關(guān)于使用vscode搭建javaweb項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2022-11-11項(xiàng)目連接nacos配置中心報(bào)錯(cuò):Client not connected, current
這篇文章主要介紹了項(xiàng)目連接nacos配置中心報(bào)錯(cuò):Client not connected, current status:STARTING的解決方案,采用了mysql作為持久化的數(shù)據(jù)庫,docker作為運(yùn)行的環(huán)境,感興趣的朋友跟隨小編一起看看吧2024-03-03Spring Boot啟動(dòng)及退出加載項(xiàng)的方法
這篇文章主要介紹了Spring Boot啟動(dòng)及退出加載項(xiàng)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Spring boot+mybatis+thymeleaf 實(shí)現(xiàn)登錄注冊(cè)增刪改查功能的示例代碼
這篇文章主要介紹了Spring boot+mybatis+thymeleaf 實(shí)現(xiàn)登錄注冊(cè)增刪改查功能的示例代碼,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07使用@Autowired 注入RedisTemplate報(bào)錯(cuò)的問題及解決
這篇文章主要介紹了使用@Autowired 注入RedisTemplate報(bào)錯(cuò)的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08