Java線程池實現(xiàn)類ThreadPoolExecutor源碼分析

線程池實現(xiàn)類ThreadPoolExecutor是在java.util.concurrent下的贯溅,從JDK1.5開始支持線程池實現(xiàn)類ThreadPoolExecutor.

該類有四個構(gòu)造函數(shù)(不含無參構(gòu)造函數(shù)),分別為:

// 常用的是這種燥滑,使用默認(rèn)的線程工廠和拒絕策略

1改鲫、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue)

// 用戶自定義線程工廠

2窗市、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory)

// 用戶自定義線程池飽和/執(zhí)行拒絕策略

3肮街、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,

RejectedExecutionHandler handler)

// 用戶自定義線程工廠和線程池飽和/執(zhí)行拒絕策略

4风题、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

其中:1、2嫉父、3構(gòu)造函數(shù)均調(diào)用this(args...)構(gòu)造函數(shù)沛硅,即構(gòu)造函數(shù)4。

Executors類提供了默認(rèn)的ThreadFactory(線程工廠),即Executors.defaultThreadFactory()绕辖,

ThreadPoolExecutor提供了默認(rèn)的線程池飽和和執(zhí)行拒絕策略摇肌,private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();

線程池實現(xiàn)類的構(gòu)造函數(shù)參數(shù):

corePoolSize(線程池核心線程大小):當(dāng)提交一個任務(wù)到線程池時,線程池會創(chuàng)建一個線程來執(zhí)行任務(wù)引镊,即使其他空閑的基本線程能夠執(zhí)行新任務(wù)也會創(chuàng)建線程朦蕴,當(dāng)任務(wù)數(shù)大于核心線程數(shù)的時候就不會再創(chuàng)建。在這里要注意一點弟头,線程池剛創(chuàng)建的時候,其中并沒有創(chuàng)建任何線程涉茧,而是等任務(wù)來才去創(chuàng)建線程赴恨,除非調(diào)用了prestartAllCoreThreads()或者prestartCoreThread()方法 ,這樣才會預(yù)先創(chuàng)建好corePoolSize個線程或者一個線程伴栓。

maximumPoolSize(線程池最大線程數(shù)):線程池允許創(chuàng)建的最大線程數(shù)伦连,如果隊列滿了,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù)钳垮,則線程池會再創(chuàng)建新的線程執(zhí)行任務(wù)惑淳。值得注意的是,如果使用了無界隊列饺窿,此參數(shù)就沒有意義了歧焦。

keepAliveTime(線程活動保持時間):此參數(shù)默認(rèn)在線程數(shù)大于corePoolSize的情況下才會起作用, 當(dāng)線程(默認(rèn)是針對非核心線程)的空閑時間達(dá)到keepAliveTime的時候就會終止肚医,直至線程數(shù)目小于corePoolSize绢馍。不過如果調(diào)用allowCoreThreadTimeOut方法向瓷,則當(dāng)線程數(shù)目小于corePoolSize的時候也會起作用.

unit(keelAliveTime的時間單位):keelAliveTime的時間單位,一共有7種舰涌,在這里就不列舉了猖任。

workQueue(阻塞隊列):阻塞隊列,用來存儲等待執(zhí)行的任務(wù)瓷耙,這個參數(shù)也是非常重要的朱躺,在這里簡單介紹一下幾個阻塞隊列。

????1)ArrayBlockingQueue:這是一個基于數(shù)組結(jié)構(gòu)的有界阻塞隊列搁痛,此隊列按照FIFO的原則對元素進(jìn)行排序室琢。

????2)LinkedBlockingQueue:一個基于鏈表結(jié)構(gòu)的阻塞隊列,此隊列按照FIFO排序元素落追,吞吐量通常要高于ArrayBlockingQueue盈滴。靜態(tài)工廠方法Executors.newFixedThreadPool()就是使用了這個隊列。

????3)SynchronousQueue:一個不存儲元素的阻塞隊列轿钠。每個插入操作必須等到另一個線程調(diào)用移除操作巢钓,否則插入操作一直處于阻塞狀態(tài)。吞吐量通常要高于LinkedBlockingQueue疗垛,靜態(tài)工廠方法Executors.newCachedThreadPool()就使用了這個隊列症汹。

????4)PriorityBlockingQueue一個具有優(yōu)先級的無阻塞隊列

handler(飽和策略)贷腕;當(dāng)線程池和隊列都滿了背镇,說明線程池已經(jīng)處于飽和狀態(tài)了,那么必須采取一種策略來處理還在提交過來的新任務(wù)泽裳。這個飽和策略默認(rèn)情況下是AbortPolicy瞒斩,表示無法處理新任務(wù)時拋出異常。共有四種飽和策略提供涮总,當(dāng)然我們也可以選擇自己實現(xiàn)飽和策略胸囱。

AbortPolicy:直接丟棄并且拋出RejectedExecutionException異常,這種是默認(rèn)的飽和策略

CallerRunsPolicy:只用調(diào)用者所在線程來運行任務(wù)瀑梗,如果線程池處于Running狀態(tài)烹笔,直接在提交任務(wù)的線程中執(zhí)行任務(wù);如果線程池不處于Running狀態(tài)抛丽,直接丟棄任務(wù)谤职。

DiscardOldestPolicy:丟棄隊列里最近的一個任務(wù),并執(zhí)行當(dāng)前任務(wù)亿鲜。

DiscardPolicy:丟棄任務(wù)并且不拋出異常允蜈,如果線程池處于Running狀態(tài),從阻塞隊列中移除頭部任務(wù)并重新調(diào)用execute方法

ThreadPoolExecutor類中的幾個關(guān)鍵屬性及方法源碼解析:

查看ThreadPoolExecutor源碼陷寝,可以發(fā)現(xiàn)锅很,有以下屬性:

//ctl是線程池主要的控制狀態(tài),是一個復(fù)合類型的變量凤跑,其中包括了兩個概念爆安。

// ctl的高3位代表線程池的狀態(tài),低29位代表workerCount

// workerCount:表示有效的線程數(shù)目(低29位)

// runState:線程池里線程的運行狀態(tài)(高3位)

// 線程池的控制狀態(tài)主要分為:RUNNING仔引、SHUTDOWN扔仓、STOP、TIDYING咖耘、TERMINATE

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));?

// COUNT_BITS用來表示線程池數(shù)量的位數(shù)翘簇,Integer.SIZE=32,因此COUNT_BITS=29儿倒,減3是為線程池運行狀態(tài)預(yù)留3位bit位版保。

private static final int COUNT_BITS = Integer.SIZE - 3;?

// 線程池最大線程數(shù)量,線程池最大線程數(shù)量為2^29-1個

private static final int CAPACITY = (1 << COUNT_BITS) - 1;?

// runState is stored in the high-order bits夫否,線程池運行狀態(tài)彻犁,存儲在ctl變量的高位(高3位)

// 高3位為111,則是在RUNNING狀態(tài)凰慈,

private static final int RUNNING = -1 << COUNT_BITS;?

// 高3位位000汞幢,則是在SHUTDOWN狀態(tài)

private static final int SHUTDOWN = 0 << COUNT_BITS;? ? ? // 0?

// 高3位為001,則是在STOP狀態(tài)

private static final int STOP = 1 << COUNT_BITS;?

// 高3位為010微谓,則是在TIDYING狀態(tài)

private static final int TIDYING = 2 << COUNT_BITS;?

// 高3位為110森篷,則是在TERMINATED狀態(tài)

private static final int TERMINATED = 3 << COUNT_BITS;?

// Packing and unpacking ctl?

// 獲取當(dāng)前運行狀態(tài),通過對CAPACITY取反豺型,然后和傳進(jìn)來的參數(shù)進(jìn)行與運算

private static int runStateOf(int c) { return c & ~CAPACITY; }?

// 獲取當(dāng)前有效的線程數(shù)量

private static int workerCountOf(int c) { return c & CAPACITY; }

?private static int ctlOf(int rs, int wc) { return rs | wc; }??

// 阻塞隊列

private final BlockingQueue?workQueue;

// 可重入鎖仲智,訪問workers集合、訪問largestPoolSize等線程池的統(tǒng)計型變量触创、執(zhí)行shutdown方法坎藐、執(zhí)行shutdownNow方法都需要獲取該鎖

private final ReentrantLock mainLock = new ReentrantLock();

// 線程池內(nèi)的有效線程

private final HashSet<Worker> workers = new HashSet<Worker>();

//和mainLock綁定的Condition對象,執(zhí)行awaitTermination方法和tryTerminate方法時使用該Condition對象

private final Condition termination = mainLock.newCondition();

// 線程工廠

private volatile ThreadFactory threadFactory;

// 線程池達(dá)到飽和后的拒絕策略

private volatile RejectedExecutionHandler handler;

線程池的控制(運行)狀態(tài)

線程池的控制狀態(tài)主要分為:RUNNING哼绑、SHUTDOWN、STOP碉咆、TIDYING抖韩、TERMINATE

每種控制狀態(tài)下,對新創(chuàng)建的線程(任務(wù))和已在阻塞隊列的線程(任務(wù))的策略都有不同疫铜,

1)RUNNING狀態(tài):線程池可以接受新任務(wù)并且處理已經(jīng)在阻塞隊列的任務(wù)茂浮。

2)SHUTDOWN狀態(tài):?線程池不接受新任務(wù),但是處理已經(jīng)在阻塞隊列的任務(wù)。

3)STOP狀態(tài):?線程池不接受新任務(wù)席揽,也不處理阻塞隊列里的任務(wù)顽馋,并且會中斷正在處理的任務(wù)。

4)TIDYING狀態(tài):線程池所有任務(wù)都被中止幌羞,workerCount(有效線程數(shù)量)是0寸谜,線程狀態(tài)轉(zhuǎn)化為TIDYING并且調(diào)用terminated()鉤子方法。

5)TERMINATE狀態(tài):terminated()鉤子方法已經(jīng)完成属桦。

線程池執(zhí)行線程(調(diào)用execute(Runnable command)方法)流程:

1熊痴、首先判斷任務(wù)是否為空,空則拋出空指針異常(if command==null throw new NullPointerException();)

2聂宾、command不為空則獲取線程池控制狀態(tài)ctl值(包括:運行狀態(tài)和有效線程數(shù))果善,判斷有效線程數(shù)是否小于corePoolSize,小于添加到worker集合當(dāng)中執(zhí)行系谐,如成功巾陕,則返回(執(zhí)行線程command任務(wù)),失敗的話再接著獲取線程池控制狀態(tài)纪他,因為只有狀態(tài)變了才會失敗鄙煤,所以重新獲取。

3止喷、判斷線程池是否處于運行狀態(tài)馆类,是的話則添加command到阻塞隊列,加入時也會再次獲取狀態(tài)并且檢測

? 狀態(tài)是否不處于運行狀態(tài)弹谁,不處于的話則將command從阻塞隊列移除乾巧,并且拒絕任務(wù)

4、如果線程池里沒有了線程(workerCountOf 方法返回值為0)预愤,則創(chuàng)建新的線程去執(zhí)行獲取阻塞隊列的任務(wù)執(zhí)行沟于。

5、如果以上都沒執(zhí)行成功植康,則需要開啟最大線程池里的線程來執(zhí)行任務(wù)旷太,失敗的話就丟棄。

線程池執(zhí)行線程的流程圖例销睁,(借用作者:KingJack文章配圖):


線程池執(zhí)行線程流程
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末供璧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子冻记,更是在濱河造成了極大的恐慌睡毒,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冗栗,死亡現(xiàn)場離奇詭異演顾,居然都是意外死亡供搀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門钠至,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葛虐,“玉大人,你說我怎么就攤上這事棉钧∮炱辏” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵掰盘,是天一觀的道長摄悯。 經(jīng)常有香客問我,道長愧捕,這世上最難降的妖魔是什么奢驯? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮次绘,結(jié)果婚禮上瘪阁,老公的妹妹穿的比我還像新娘。我一直安慰自己邮偎,他們只是感情好管跺,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著禾进,像睡著了一般豁跑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泻云,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天艇拍,我揣著相機與錄音,去河邊找鬼宠纯。 笑死卸夕,一個胖子當(dāng)著我的面吹牛邢锯,可吹牛的內(nèi)容都是我干的笤喳。 我是一名探鬼主播裕寨,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼与柑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纯露?” 一聲冷哼從身側(cè)響起沈堡,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坡锡,失蹤者是張志新(化名)和其女友劉穎猴蹂,沒想到半個月后勃黍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡晕讲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年覆获,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓢省。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡弄息,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勤婚,到底是詐尸還是另有隱情摹量,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布馒胆,位于F島的核電站缨称,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祝迂。R本人自食惡果不足惜睦尽,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望型雳。 院中可真熱鬧当凡,春花似錦、人聲如沸纠俭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冤荆。三九已至朴则,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钓简,已是汗流浹背乌妒。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涌庭,地道東北人芥被。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像坐榆,于是被迫代替她去往敵國和親拴魄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容