上面這段代碼一直在用,面試的時候也經常被問到,卻從未深究過,不知道線程池到底是怎么回事,今天看看源代碼,一探其究竟
線程池主要控制的狀態是ctl,它是一個原子的整數,其包含兩個概念字段:
- workerCount:有效的線程數量
- runState:線程池的狀態
為了在一個整型值里面包含這兩個字段,我們限制workerCount最多2的29次方減1
runState的值有這樣幾種:
- RUNNING: 接受新的任務,並處理隊列中的任務
- SHUTDOWN:不接受新的任務,繼續處理隊列中的任務
- STOP: 不接受新的任務,也不處理隊列中的任務,並且中斷正在處理的任務
- TIDYING: 所有任務都結束了,workerCount是0,通過調用terminated()方法轉換狀態
- TERMINATED:terminated()方法已經完成
狀態之間的轉換時這樣的:
RUNNING -> SHUTDOWN
調用shutdown()方法,或者隱式的調用finalize()方法
(RUNNING or SHUTDOWN) -> STOP
調用shoutdownNow()方法
SHUTDOWN -> TIDYING
當隊列和池都是空的時候
STOP -> TIDYING
當池是空的時候
TIDYING -> TERMINATED
當terminated()方法調用完成時
Integer.SIZE=31
Integer.SIZE - 3 = 29
所以,COUNT_BITS = 29
高3位存儲runState
接下來看最復雜的那個構造方法
參數詳解
- corePoolSize:保持在池中的線程數(PS:即使它們處於空閑狀態)
- maximumPoolSize:池中允許的最大線程數
- keepAliveTime:當線程數超過核心線程數的時候,超出的線程的最大生存時間
- unit:keepAliveTime的單位
- workQueue:維護待處理的任務的隊列
- threadFactory:創建線程的工廠
1、如果正在運行的線程數少於核心線程數,則新建一個線程去運行這個任務
2、如果工作隊列沒有滿,則放到工作隊列中
3、如果工作隊列已滿(PS:workQueue.offer(command)返回false),則再新建線程
4、若線程數已經達到最大線程數則reject(command)
在前面execute方法中,有3處調用了addWork()方法
第一處,如果當前有效線程數少於核心線程數,則調用之,此時線程數的邊界是核心線程數
第二處,如果當前有效線程數超過核心線程數,並且將新任務放到隊列中,此時有效線程數是0,則創建一個新線程
第三處,如果當前有效線程數超過核心線程數,並且隊列已經放滿了,並且有效線程數小於最大線程數,此時調用以創建新線程,
當前的有效線程都在works里面,而works里面放的是Worker對象,接下來看Worker
前一步中,線程的start()方法,線程運行的時候執行run()方法,而Worker繼承Runnable並重新run()方法,run()方法又調用外部的runWorker()方法,所以,在線程池中新創建的線程運行時調用的是runWorker()方法
runWorker()方法循環的從隊列中取出任務並執行它。也就是說,池中所有的線程都是在創建的時候如果傳進來新任務,則先執行新任務,然后循環從隊列中取出任務並執行
接下來,看Executors中常見的幾種線程池的區別
可以看到
newSingleThreadExecutor:核心線程數和最大線程數都是1,隊列是LinkedBlockingQueue
newFixedThreadPool:核心線程數和最大線程數相等,超過核心線程數的空閑線程生存時間是0,隊列是LinkedBlockingQueue
newCachedThreadPool:核心線程數是0,最大線程數是Integer.MAX_VALUE,空閑線程生存時間是1min,隊列是SynchronousQueue
舉例說明
栗子1
假設,newFixedThreadPool的時候固定線程是2,所有,最大線程和核心線程都是2,而它的工作隊列用的是LinkedBlockingQueue。
當第1個任務提交過來,新創建一個線程並執行任務,此時線程池中有1個線程;
當第2個任務提交過來,再新建一下線程並執行任務,此時線程池中有2個線程;
當第3個任務提交過來,放入工作隊列中,然后由線程池中的兩個線程輪詢隊列處理
當第n個任務提交過來,仍然放入工作隊列
栗子2
假設,newCachedThreadPool,需要注意的是,它的工作隊列是SynchronousQueue,這種隊列的特性是一個線程向隊列中放元素的時候必須同時另一個線程從隊列中取出元素,否則是放不進去的
當第1個任務提交過來,線程池中的線程數為0,於是,新建一個線程,並執行任務,此時,線程池中有1個線程;
當第2個任務提交過來,由於無法放到隊列中,於是再創建一個線程,此時線程池中有2個線程;
當第3個任務提交過來,若前兩個線程有一個處理完了,這個時候由於是循環從隊列中取,所有可以放到工作隊列中