ScheduledThreadPoolExecutor主要用來定期執(zhí)行任務(wù)沛厨,或者是在給定的延遲之后運行任務(wù)吨拗。它的功能與Timer類似,但是比起Timer恕刘,ScheduledThreadPoolExecutor功能更強(qiáng)大鹃锈,使用也更靈活攀例。
ScheduledThreadPoolExecutor與Timer區(qū)別:
Timer對應(yīng)單個后臺線程振坚,所有的任務(wù)都由同一個線程調(diào)度购撼,因此所有的任務(wù)都是串行執(zhí)行的,前一個任務(wù)的延遲或者異常都將會影響到之后的任務(wù)匕累;
ScheduledThreadPoolExecutor對應(yīng)多個后臺線程陵刹,每一個調(diào)度的任務(wù)都將由線程池中的一個線程去執(zhí)行,在同一時刻欢嘿,任務(wù)并發(fā)執(zhí)行衰琐,并且它們之間不會相互干擾。
ScheduledThreadPoolExecutor源碼分析
在開始分析源碼具體實現(xiàn)之前际插,先給一個簡單的ScheduledThreadPoolExecutor使用案例:
ScheduledThreadPoolExecutor聲明;
調(diào)用scheduleAtFixedRate方法和scheduleWithFixedDelay方法提交定時任務(wù)task显设。
類聲明
在看構(gòu)造方法之前先來看看ScheduledThreadPoolExecutor類聲明:
從ScheduledThreadPoolExecutor類聲明可以看出:
ScheduledThreadPoolExecutor是ThreadPoolExecutor的子類框弛,并且實現(xiàn)了接口ScheduledExecutorService;
ScheduledThreadPoolExecutor是另外一種線程池捕捂,它同ThreadPoolExecutor擁有相同的特性瑟枫,但是又略有不同,具體的不同之處會在后文做詳細(xì)的介紹指攒。
構(gòu)造方法
ScheduledThreadPoolExecutor提供3個構(gòu)造方法以供使用者使用:
從構(gòu)造方法可以看出慷妙,ScheduledThreadPoolExecutor使用DelayQueue來作為線程池的工作隊列,由于DelayQueue是無界隊列允悦,根據(jù)線程池的工作原理膝擂,核心參數(shù)maximumPoolSize在ScheduledThreadPoolExecutor中是沒有什么意義的。
總的來說隙弛,ScheduledThreadPoolExecutor為了實現(xiàn)周期性執(zhí)行任務(wù)架馋,對ThreadPoolExecutor做了以下改動:
工作隊列使用DelayQueue;
任務(wù)提交之后統(tǒng)統(tǒng)都進(jìn)工作隊列全闷;
獲取任務(wù)的方式改變叉寂,執(zhí)行了任務(wù)之后,也增加了額外的處理总珠,具體的改變后文會一一給出詳細(xì)的分析屏鳍。
任務(wù)提交與調(diào)度
ScheduledThreadPoolExecutor中最常用的任務(wù)提交的方法有兩個:ScheduleAtFixedRate方法和ScheduleWithFixedDelay方法。
具體的執(zhí)行流程如下:
參數(shù)校驗局服,不合法參數(shù)拋出異常钓瞭;
構(gòu)造task;
調(diào)用delayedExecute方法進(jìn)行后續(xù)相關(guān)處理淫奔。
接下來我們先來分析ScheduledThreadPoolExecutor的調(diào)度任務(wù)的最小單位ScheduledFutureTask相關(guān)實現(xiàn)降淮。
ScheduledFutureTask
-
成員變量
ScheduledFutureTask包含3個成員變量:
sequenceNumber:任務(wù)被添加到ScheduledThreadPoolExecutor中的序號;
time:任務(wù)將要被執(zhí)行的具體時間;
period:任務(wù)執(zhí)行的間隔周期佳鳖。
ScheduledThreadPoolExecutor會把待執(zhí)行的任務(wù)放到工作隊列DelayQueue中霍殴,DelayQueue封裝了一個PriorityQueue,PriorityQueue會對隊列中的ScheduledFutureTask進(jìn)行排序系吩,具體的排序算法實現(xiàn)如下:
-
compareTo實現(xiàn)
首先按照time排序来庭,time小的排在前面,time大的排在后面穿挨;
如果time相同月弛,按照sequenceNumber排序,sequenceNumber小的排在前面科盛,sequenceNumber大的排在后面帽衙,換句話說,如果兩個task的執(zhí)行時間相同贞绵,優(yōu)先執(zhí)行先提交的task厉萝。
-
任務(wù)調(diào)度之run方法實現(xiàn)
run方法是調(diào)度task的核心,task的執(zhí)行實際上是run方法的執(zhí)行榨崩。
具體執(zhí)行流程如下:
步驟1:判斷當(dāng)前task是否可以執(zhí)行谴垫,如果不能執(zhí)行,調(diào)用cancel方法取消task執(zhí)行母蛛,否則翩剪,跳轉(zhuǎn)到步驟2;
步驟2:判斷當(dāng)前task是否到達(dá)執(zhí)行時間點彩郊,如果到達(dá)前弯,執(zhí)行該task,否則跳轉(zhuǎn)到步驟3秫逝;
步驟3:重置狀態(tài)博杖,計算任務(wù)下次執(zhí)行時間,重新把任務(wù)添加到工作隊列中筷登,讓該任務(wù)可重復(fù)執(zhí)行剃根。
看完task之后,我們接下看看看構(gòu)造好task之后的delayedExecute方法相關(guān)實現(xiàn)前方。
** delayedExecute實現(xiàn)**
delayedExecute方法主要完成了這些操作:
將task添加到工作隊列狈醉;
調(diào)用ensurePrestart()方法做預(yù)處理,該方法實現(xiàn)在線程池一文中做過詳細(xì)分析在這里就不再做贅述了惠险。
任務(wù)調(diào)度小結(jié)
到這里為止苗傅,ScheduledThreadPoolExecutor的task執(zhí)行過程可以總結(jié)為下圖:
- 步驟1:線程1從工作隊列DelayQueue中獲取已到期的task;
- 步驟2:線程1執(zhí)行該task班巩;
- 步驟3:線程1修改ScheduledFutureTask的time變量為下次被執(zhí)行的時間渣慕;
- 步驟4:線程1將修改后的task重新放回DelayQueue中嘶炭。
接下來我們來詳細(xì)看看各個步驟DelayQueue具體相關(guān)實現(xiàn)。
任務(wù)獲取 - take實現(xiàn)
具體執(zhí)行步驟如下:
獲取Lock逊桦;
從優(yōu)先隊列中獲取任務(wù):
步驟1:如果PriorityQueue為空眨猎,當(dāng)前線程到Condition中等待,否則執(zhí)行步驟2强经;
步驟2:如果PriorityQueue的第一個元素的時間比當(dāng)前時間小睡陪,獲取該任務(wù),否則匿情,線程到Condition中等待兰迫;
步驟3:獲取任務(wù)成功后,如果PriorityQueue不為空炬称,喚醒等待在Condition中的所有線程汁果。
- 釋放Lock。
ScheduledThreadPoolExecutor的getTask()方法會無限循環(huán)獲取task玲躯,直到線程從PriorityQueue中獲取到一個元素后据德,才會退出無限循環(huán)。
任務(wù)添加 - add實現(xiàn)
add方法的核心是offer方法調(diào)用府蔗,我們來看看offer方法的具體實現(xiàn):
具體執(zhí)行步驟如下:
獲取Lock晋控;
添加任務(wù):
向PriorityQueue添加任務(wù)汞窗;
如果待添加的任務(wù)是PriorityQueue的第一個元素姓赤,喚醒在Condition中等待的所有線程。
- 釋放Lock仲吏。