Java線程池復(fù)用線程的秘密你知道嗎
前言
我們都知道線程池可以幫我們管理線程,重復(fù)利用線程執(zhí)行不同的任務(wù)。正常情況下,我們創(chuàng)建的線程執(zhí)行完任務(wù)后就會(huì)自行銷毀,那么線程池是如何做到復(fù)用線程的呢?
源碼探究
我們從線程池ThreadPoolExecutor源碼入手,一探究竟。為了突出重點(diǎn),以下的方法源碼過濾了部分無關(guān)代碼,以求邏輯清晰。
execute方法
那就從線程池執(zhí)行的execute方法入手吧!來看一下方法的源碼
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.小于核心線程數(shù)時(shí),創(chuàng)建線程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.達(dá)到核心線程數(shù),不超過隊(duì)列界限時(shí),添加到隊(duì)列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3.隊(duì)列已滿,不超過最大線程數(shù)時(shí),創(chuàng)建線程
else if (!addWorker(command, false))
//4.達(dá)到最大線程數(shù)時(shí),執(zhí)行拒絕策略
reject(command);
}
線程池執(zhí)行的4個(gè)步驟相信大家已經(jīng)有所了解,這里我們只看添加線程的方法addWorker()
addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//1.創(chuàng)建Worker,傳入任務(wù)
w = new Worker(firstTask);
//2.取出執(zhí)行任務(wù)的線程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int c = ctl.get();
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
if (t.getState() != Thread.State.NEW)
throw new IllegalThreadStateException();
workers.add(w);
workerAdded = true;
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//3.執(zhí)行線程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
參數(shù)解釋:
core:true表示添加的是核心線程,false表示添加的非核心線程
這里大家只需要關(guān)心這3行加注釋的代碼就可以了
就是Worker管理了創(chuàng)建的線程和這個(gè)線程執(zhí)行的第一個(gè)任務(wù),并且在addWorker方法中調(diào)用線程的start方法,開啟線程執(zhí)行了任務(wù)。下面我們看看Worker這個(gè)類
Worker類
實(shí)現(xiàn)了Runnable接口
Worker 是線程池ThreadPoolExecutor的內(nèi)部類,實(shí)現(xiàn)了Runnable接口
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
重要屬性
他有兩個(gè)核心關(guān)鍵的屬性,即封裝了線程池的線程和要執(zhí)行的任務(wù),達(dá)到了線程和任務(wù)解耦的目的。
final Thread thread; Runnable firstTask;
構(gòu)造方法
addWorker方法會(huì)執(zhí)行創(chuàng)建一個(gè)worker
w = new Worker(firstTask);
看一下Worker的構(gòu)造方法
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
可以看到 新創(chuàng)建的Worker本身也是一個(gè)Runnable,他的thread傳的runnable任務(wù)就是worker本身
在addWorker方法,最終會(huì)取到worker的thread屬性,然后啟動(dòng)這個(gè)thread
w = new Worker(firstTask); final Thread t = w.thread; ... ... t.start();
run方法
剛才介紹過,worker的thread的runnable參數(shù)傳的就是worker本身,就是調(diào)的worker的run方法,現(xiàn)在我們來看最核心的worker的run方法
public void run() {
runWorker(this);
}
調(diào)的是ThreadPoolExecutor的runWorker方法
final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
try {
while (task != null || (task = getTask()) != null)
{
...
task.run();
...
可以看到runWorker核心是一個(gè)while循環(huán),執(zhí)行了第一個(gè)task之后,就不停的從隊(duì)列中取任務(wù),直到?jīng)]有任務(wù)了才會(huì)執(zhí)行完,銷毀線程
執(zhí)行流程
execute方法調(diào)用addWorker方法,并且執(zhí)行worker.thread.start()開啟線程
? ——》worker.thread 執(zhí)行worker本身的run方法(worker實(shí)現(xiàn)了Runnable接口)
? ——》執(zhí)行ThreadPoolExecutor的runWorker方法,是個(gè)while循環(huán),執(zhí)行完worker本身的第一個(gè)任務(wù)之后,就不停從隊(duì)列取任務(wù),直到?jīng)]有任務(wù),執(zhí)行完,退出循環(huán),銷毀
總結(jié)
線程池將線程和任務(wù)進(jìn)行解耦,線程是線程,任務(wù)是任務(wù),擺脫了之前通過 Thread 創(chuàng)建線程時(shí)的一個(gè)線程必須對(duì)應(yīng)一個(gè)任務(wù)的限制。
在線程池中,同一個(gè)線程可以從阻塞隊(duì)列中不斷獲取新任務(wù)來執(zhí)行,其核心原理在于線程池對(duì) Thread 進(jìn)行了封裝,并不是每次執(zhí)行任務(wù)都會(huì)調(diào)用 Thread.start() 來創(chuàng)建新線程,而是讓每個(gè)線程去執(zhí)行一個(gè)“循環(huán)任務(wù)”,在這個(gè)“循環(huán)任務(wù)”中不停的檢查是否有任務(wù)需要被執(zhí)行,如果有則直接執(zhí)行,也就是調(diào)用任務(wù)中的 run 方法,將 run 方法當(dāng)成一個(gè)普通的方法執(zhí)行,通過這種方式將只使用固定的線程就將所有任務(wù)的 run 方法串聯(lián)起來。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java并發(fā)線程之線程池的知識(shí)總結(jié)
這篇文章主要介紹了Java并發(fā)線程之線程池的知識(shí)總結(jié),幫助大家更好的理解和學(xué)習(xí)Java并發(fā)線程的相關(guān)內(nèi)容,感興趣的朋友可以了解下2021-01-01
SVN報(bào)錯(cuò):Error Updating changes:svn:E155037的解決方案
今天小編就為大家分享一篇關(guān)于SVN報(bào)錯(cuò):Error Updating changes:svn:E155037的解決方案,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
java環(huán)境變量path和classpath的配置
這篇文章主要為大家詳細(xì)介紹了java系統(tǒng)環(huán)境變量path和classpath的配置過程,感興趣的小伙伴們可以參考一下2016-07-07
簡(jiǎn)單易懂的Java Map數(shù)據(jù)添加指南
Java提供了多種方法來往Map中添加數(shù)據(jù),開發(fā)者可以根據(jù)具體需求選擇合適的方法,需要的朋友可以參考下2023-11-11
springboot 實(shí)現(xiàn)bean手動(dòng)注入操作
這篇文章主要介紹了springboot 實(shí)現(xiàn)bean手動(dòng)注入操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01

