關(guān)于線(xiàn)程池創(chuàng)建、執(zhí)行、銷(xiāo)毀的原理及分析
線(xiàn)程池的執(zhí)行原理
假設(shè)最大核心數(shù)是2,非核心線(xiàn)程數(shù)為1,隊(duì)列長(zhǎng)度是3
來(lái)第一個(gè)任務(wù)的時(shí)候,沒(méi)有工作線(xiàn)程在工作,需要?jiǎng)?chuàng)建一個(gè)
來(lái)第二個(gè)任務(wù)的時(shí)候,發(fā)現(xiàn)當(dāng)前核心線(xiàn)程數(shù)小于最大核心線(xiàn)程數(shù),所以繼續(xù)創(chuàng)建線(xiàn)程來(lái)處理任務(wù)
當(dāng)來(lái)第三個(gè)任務(wù)的時(shí)候,發(fā)現(xiàn)當(dāng)前核心線(xiàn)程數(shù)已經(jīng)等于最大核心線(xiàn)程數(shù)了,所以把新來(lái)的任務(wù)放到taskQueue中
后面來(lái)第四個(gè)、第五個(gè)任務(wù)也會(huì)放在taskQueue中
當(dāng)來(lái)第六個(gè)任務(wù)的時(shí)候,發(fā)現(xiàn)taskQueue已經(jīng)滿(mǎn)了,所以會(huì)創(chuàng)建一個(gè)非核心線(xiàn)程來(lái)處理任務(wù)
當(dāng)來(lái)第七個(gè)任務(wù)的時(shí)候,因?yàn)榫€(xiàn)程數(shù)量到最大限度了,taskQueue也滿(mǎn)了,所以就會(huì)走拒絕策略,把其中一個(gè)任務(wù)給拋棄掉,具體拋棄哪個(gè)需要根據(jù)選擇的拒絕策略來(lái)定。
創(chuàng)建線(xiàn)程這里需要考慮并發(fā)的問(wèn)題,即多個(gè)任務(wù)同時(shí)過(guò)來(lái)了,需要串行創(chuàng)建線(xiàn)程,否則,可能會(huì)導(dǎo)致超賣(mài)的情況(即創(chuàng)建的線(xiàn)程超過(guò)了最大線(xiàn)程數(shù)),具體是通過(guò)CAS樂(lè)觀鎖實(shí)現(xiàn),代碼解釋如下:
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get();//獲取當(dāng)前線(xiàn)程池的控制狀態(tài)。 int rs = runStateOf(c);//獲取當(dāng)前線(xiàn)程池的運(yùn)行狀態(tài)。 // Check if queue empty only if necessary. //下面這個(gè)條件判斷用于判斷線(xiàn)程池是否處于關(guān)閉狀態(tài),并且任務(wù)隊(duì)列不為空。如果線(xiàn)程池的狀態(tài)大于等于SHUTDOWN,并且不滿(mǎn)足線(xiàn)程池狀態(tài)為SHUTDOWN、首個(gè)任務(wù)為null且任務(wù)隊(duì)列為空的條件,則返回false。這個(gè)判斷是為了確保在線(xiàn)程池關(guān)閉時(shí),不再添加新的工作線(xiàn)程。 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c);//獲取當(dāng)前線(xiàn)程池中的工作線(xiàn)程數(shù)量。 //這個(gè)條件判斷用于判斷工作線(xiàn)程數(shù)量是否達(dá)到上限。如果工作線(xiàn)程數(shù)量大于等于CAPACITY(工作線(xiàn)程數(shù)量的上限)或者大于等于核心線(xiàn)程數(shù)(如果core為true)或最大線(xiàn)程數(shù)(如果core為false),則返回false。這個(gè)判斷是為了確保工作線(xiàn)程數(shù)量不超過(guò)線(xiàn)程池的限制。 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //嘗試通過(guò)CAS(比較并交換)操作增加工作線(xiàn)程數(shù)量。如果成功增加工作線(xiàn)程數(shù)量,則跳出循環(huán),繼續(xù)執(zhí)行后續(xù)邏輯。 if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // 重新讀取當(dāng)前線(xiàn)程池的控制狀態(tài)。 //再次判斷線(xiàn)程池的運(yùn)行狀態(tài)是否發(fā)生了變化。如果運(yùn)行狀態(tài)發(fā)生了變化,則繼續(xù)重試內(nèi)部循環(huán)。這個(gè)判斷是為了處理在CAS操作過(guò)程中,線(xiàn)程池的狀態(tài)發(fā)生了變化的情況。 if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } ...創(chuàng)建線(xiàn)程的邏輯忽略... }
線(xiàn)程執(zhí)行
花開(kāi)兩朵,各表一枝。
上面說(shuō)了任務(wù)來(lái)了之后,是怎么創(chuàng)建線(xiàn)程的,又是怎么暫存任務(wù)的。這一節(jié)介紹一下線(xiàn)程是怎么執(zhí)行任務(wù)的,以及在不用的時(shí)候,線(xiàn)程怎么被銷(xiāo)毀或者?;睢?/p>
線(xiàn)程池創(chuàng)建線(xiàn)程并調(diào)了thread的start方法之后,該線(xiàn)程會(huì)走到下面的runWorker方法。
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
當(dāng)一個(gè)線(xiàn)程執(zhí)行完就會(huì)自動(dòng)退出,但是我們知道線(xiàn)程池中的核心線(xiàn)程會(huì)一直存活著,想要一直存活,不退出程序就可以了,即循環(huán),從上面的代碼也可以看出來(lái)確實(shí)是這樣的。但是還有一個(gè)疑問(wèn),核心線(xiàn)程是一直存活的,但是非核心線(xiàn)程在一定情況是會(huì)銷(xiāo)毀的,他們用的是一套代碼邏輯,該怎么實(shí)現(xiàn)呢?關(guān)鍵點(diǎn)就在getTask這個(gè)方法中。
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //銷(xiāo)毀線(xiàn)程需要滿(mǎn)足這兩個(gè)條件:1. (允許核心線(xiàn)程銷(xiāo)毀 || 線(xiàn)程數(shù)大于核心線(xiàn)程數(shù))&& 達(dá)到了銷(xiāo)毀時(shí)間;2. 任務(wù)隊(duì)列中沒(méi)有任務(wù)了 if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) // 這里返回了null,外層方法就跳出了while循環(huán),從而結(jié)束該線(xiàn)程 return null; continue; } try { // 超時(shí)時(shí)間就是在這里設(shè)置的,如果允許超時(shí)銷(xiāo)毀,那么就用poll進(jìn)行拉取任務(wù),超過(guò)了keepAliveTime就返回null。take是阻塞性等待 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
從上面的解析可以看出,如果隊(duì)列中沒(méi)有任務(wù)時(shí),小于核心數(shù)的線(xiàn)程(核心線(xiàn)程數(shù)不銷(xiāo)毀的情況下)會(huì)一直阻塞在獲取任務(wù)的方法,直到返回任務(wù)。(判斷阻塞時(shí)并沒(méi)有核心線(xiàn)程和非核心線(xiàn)程的概念,只要保證創(chuàng)建出來(lái)的線(xiàn)程銷(xiāo)毀到符合預(yù)期數(shù)量就ok)。而且執(zhí)行完后 會(huì)繼續(xù)循環(huán)執(zhí)行g(shù)etTask的邏輯,不斷的處理任務(wù)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis-plus在項(xiàng)目中的簡(jiǎn)單應(yīng)用
Mybatis-plus是Spring框架中OOM的一大利器,其簡(jiǎn)單易用參考官網(wǎng)文檔即可很快上手,本文主要介紹了邏輯刪除,自動(dòng)填充,分頁(yè)插件等的簡(jiǎn)單使用,感興趣的可以了解一下2021-07-07詳解Spring batch 入門(mén)學(xué)習(xí)教程(附源碼)
本篇文章主要介紹了Spring batch 入門(mén)學(xué)習(xí)教程(附源碼),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11詳解使用Jenkins部署Spring Boot項(xiàng)目
這篇文章主要介紹了詳解使用Jenkins部署Spring Boot,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11Java在OJ時(shí)運(yùn)行超時(shí)的問(wèn)題解決方案
Java語(yǔ)言什么都好,就是在OJ的時(shí)候真的是太慢了,今天來(lái)講解一種讓Java運(yùn)行速度快速提高的方法,感興趣的朋友一起看看吧2023-11-11Spring之spring-context-indexer依賴(lài)詳解
這篇文章主要介紹了Spring之spring-context-indexer依賴(lài)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Mybatis如何從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)存為L(zhǎng)ist類(lèi)型(存為model)
這篇文章主要介紹了Mybatis如何從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)存為L(zhǎng)ist類(lèi)型(存為model),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01如何用注解的方式實(shí)現(xiàn)Mybatis插入數(shù)據(jù)時(shí)返回自增的主鍵Id
這篇文章主要介紹了如何用注解的方式實(shí)現(xiàn)Mybatis插入數(shù)據(jù)時(shí)返回自增的主鍵Id,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07