java.util.Timer:線程的工具谒养,用于在后臺(tái)線程中安排將來執(zhí)行的任務(wù)滔岳∽そ觯可以安排一次性執(zhí)行任務(wù),或定期重復(fù)執(zhí)行任務(wù)摆舟。任務(wù)應(yīng)該是能快速完成的亥曹。這個(gè)類是線程安全的:多線程可以共享一個(gè)Timer對(duì)象邓了,而不需要外部同步。
這個(gè)類可以擴(kuò)展到大量同時(shí)計(jì)劃的任務(wù)(數(shù)千個(gè)都沒有問題)媳瞪。在內(nèi)部骗炉,它使用二進(jìn)制堆來表示其任務(wù)隊(duì)列,因此調(diào)度任務(wù)的成本為O(log n)蛇受,其中n是并發(fā)計(jì)劃任務(wù)的數(shù)量句葵。
下面基于JDK1.8源碼全面分析Timer的實(shí)現(xiàn)。
來看一下Timer的屬性:
TaskQueue queue = new TaskQueue()兢仰;//任務(wù)隊(duì)列
TimerThread thread = new TimerThread(queue);//持有任務(wù)隊(duì)列的Thread乍丈。
先簡(jiǎn)單看一下TaskQueue和TimerThread的定義。
TaskQueue:表示一個(gè)計(jì)時(shí)器任務(wù)隊(duì)列把将,一個(gè)TimerTasks的優(yōu)先級(jí)隊(duì)列轻专,通過nextExecutionTime字段排序,內(nèi)部使用堆實(shí)現(xiàn)察蹲。
屬性:
TimerTask[]queue = new TimerTask[128];//采用堆排序的數(shù)組
int size = 0;代表優(yōu)先隊(duì)列中任務(wù)數(shù)请垛;
TimerThread:繼承自Thread。相當(dāng)于一個(gè)“助手類”洽议,實(shí)現(xiàn)了定時(shí)器的任務(wù)執(zhí)行線程宗收,該線程功能包括等待定時(shí)器隊(duì)列上的任務(wù),在它們觸發(fā)時(shí)執(zhí)行它們绞铃,重新安排重復(fù)任務(wù)镜雨,并刪除取消的任務(wù),并從隊(duì)列中去除非重復(fù)任務(wù)儿捧。
屬性:
下面看一下Timer的構(gòu)造函數(shù):
構(gòu)造一個(gè)Timer的對(duì)象荚坞,會(huì)觸發(fā)thread,那么我們看一下TimerThread干了些什么菲盾。
主要邏輯:
1颓影、在queue上加同步器鎖,當(dāng)queue為空且還有線程持有timer對(duì)象懒鉴,Thread wait诡挂。直到得到notify通知,這個(gè)notify是當(dāng)有元素進(jìn)入queue或者newTasksMayBeScheduled被置為false時(shí)會(huì)觸發(fā)临谱。
2璃俗、如果queue為空,說明沒有需要處理的任務(wù)悉默,跳出循環(huán)城豁。
3、從queue取最近將要觸發(fā)的任務(wù)抄课,如果這個(gè)任務(wù)是CANCELLED狀態(tài)唱星,把它從queue中移除雳旅,并continue;否則间聊,如果任務(wù)沒有達(dá)到觸發(fā)時(shí)間攒盈,wait(觸發(fā)間隔時(shí)間)。當(dāng)任務(wù)達(dá)到觸發(fā)時(shí)間哎榴,如果這個(gè)任務(wù)不是重復(fù)跑的型豁,把它從queue中移除,并把這個(gè)任務(wù)的狀態(tài)置為EXECUTED叹话。如果它是需要重復(fù)執(zhí)行的偷遗,會(huì)設(shè)置它的下一次觸發(fā)時(shí)間。然后執(zhí)行任務(wù)的run方法執(zhí)行實(shí)際的邏輯驼壶。執(zhí)行完了之后,因?yàn)槭且粋€(gè)while的無限循環(huán)喉酌,又會(huì)開始上述邏輯判斷热凹。
在上述第3條中,其實(shí)我們是依賴queue維護(hù)了一個(gè)最近觸發(fā)時(shí)間的小根堆泪电,我們看下queue的幾個(gè)核心方法:
任務(wù)加入隊(duì)列:
先做容量判斷:數(shù)組將滿時(shí)擴(kuò)大為原來兩倍般妙;否則任務(wù)加入隊(duì)列,注意第一個(gè)任務(wù)是放在queue[1]的位置相速;然后做fixUp(size)碟渺;
這個(gè)方法實(shí)際是在構(gòu)建一個(gè)小根堆,傳入的k即為當(dāng)前隊(duì)列任務(wù)數(shù)量突诬。且任務(wù)從數(shù)組下標(biāo)1位置開始存放苫拍。當(dāng)隊(duì)列容量大于1時(shí),先取size/2對(duì)應(yīng)的元素與size對(duì)應(yīng)的元素做比較旺隙,如果queue[size/2]的下一次觸發(fā)時(shí)間>queue[size]的下一次觸發(fā)時(shí)間,則把queue[size/2]與queue[size]對(duì)應(yīng)的任務(wù)做交換绒极。循環(huán)直到數(shù)組下標(biāo)為1的元素。這樣就把最近觸發(fā)的任務(wù)放到了數(shù)組1的位置蔬捷,最后觸發(fā)的任務(wù)放到了數(shù)組末尾垄提。
獲取當(dāng)前隊(duì)列最近將要執(zhí)行的任務(wù)
移除當(dāng)前隊(duì)列第一個(gè)任務(wù)
將隊(duì)列最后一個(gè)任務(wù)賦給queue[1],最后一個(gè)位置設(shè)為null周拐,任務(wù)總數(shù)減一铡俐;關(guān)鍵我們看一下fixDown(1)做了什么。
因?yàn)殛?duì)首任務(wù)被置為了隊(duì)尾的任務(wù)妥粟,則它肯定破壞了最小堆审丘,這個(gè)方法就是要重新建立小根堆:
1、取2k對(duì)應(yīng)的節(jié)點(diǎn)罕容,因?yàn)閗的孩子節(jié)點(diǎn)是2k和2k+1,
2备恤、先判斷2k和2k+1對(duì)應(yīng)的值稿饰,取他們中小的,設(shè)置j的值
3露泊、比較queue[k]與queue[j]喉镰,如果執(zhí)行時(shí)間不是小于關(guān)系,交換這兩個(gè)任務(wù)惭笑,讓下次執(zhí)行時(shí)間越大的元素下移
4侣姆、循環(huán)直到隊(duì)列最后位置,這樣的話沉噩,執(zhí)行時(shí)間離現(xiàn)在最久的元素被放到了隊(duì)列末尾捺宗;
重復(fù)執(zhí)行的任務(wù)重新設(shè)置其下次執(zhí)行時(shí)間:
更新當(dāng)前隊(duì)首的任務(wù)的下次執(zhí)行執(zhí)行,同時(shí)重新調(diào)整小根堆川蒙。
目前我們還只是看了Timer的構(gòu)造函數(shù)蚜厉,下面我們來看Timer設(shè)置任務(wù)調(diào)度的幾個(gè)方法:
schedule(TimerTasktask, long delay);
schedule(TimerTasktask, Date time);
schedule(TimerTasktask, long delay, long period);
schedule(TimerTask task, Date firstTime, long period);
以上四個(gè)方法分別表示觸發(fā)一次和按頻率觸發(fā)多次;
scheduleAtFixedRate(TimerTasktask, long delay, long period);
scheduleAtFixedRate(TimerTasktask, Date firstTime, long period)
按固定的頻率觸發(fā)多次畜眨;
那么這兩類方法都有按頻率重復(fù)執(zhí)行的邏輯昼牛,他們有什么差別呢?
我們看一下代碼:
一個(gè)傳的是正數(shù)一個(gè)傳的是負(fù)數(shù)康聂。而在TimerThread的mainLoop方法中贰健,
可以看到如果period<0,下次調(diào)度時(shí)間是當(dāng)前時(shí)間+間隔時(shí)間(減一個(gè)負(fù)數(shù)就是加一個(gè)正數(shù))恬汁,否則下次調(diào)度時(shí)間是下次執(zhí)行時(shí)間+間隔時(shí)間伶椿,就是說他們下次觸發(fā)的時(shí)間維度不一樣。
以上兩類方法最終都會(huì)調(diào)用
設(shè)置task的屬性氓侧,將其加入到queue中脊另,并且如果這個(gè)task是隊(duì)列的第一個(gè)任務(wù),notify阻塞在queue上面的thread甘苍。整個(gè)入隊(duì)的過程持有queue的監(jiān)視器鎖尝蠕。
下面我們看一下TimerTask,它實(shí)現(xiàn)了Runnable接口载庭,其run方法由子類去實(shí)現(xiàn)看彼,它有狀態(tài)字段state表示任務(wù)的執(zhí)行狀態(tài),包括:
默認(rèn)狀態(tài)是VIRGIN囚聚。
取消一個(gè)任務(wù)則調(diào)用其cancel方法
只是簡(jiǎn)單把任務(wù)狀態(tài)置為CANCELLED靖榕。
總結(jié):
Timer通過TimerThread循環(huán)觸發(fā)將要執(zhí)行的任務(wù),通過TaskQueue維護(hù)一個(gè)待執(zhí)行任務(wù)列表顽铸,TaskQueue內(nèi)部通過一個(gè)小根堆實(shí)現(xiàn)待執(zhí)行任務(wù)的排序茁计。撥開底層,我們對(duì)Timer這個(gè)調(diào)度器能有一個(gè)更深的理解谓松。