java.util.Timer全面解析

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è)更深的理解谓松。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末星压,一起剝皮案震驚了整個(gè)濱河市践剂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌娜膘,老刑警劉巖逊脯,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異竣贪,居然都是意外死亡军洼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門演怎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匕争,“玉大人,你說我怎么就攤上這事爷耀「噬#” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵畏纲,是天一觀的道長(zhǎng)扇住。 經(jīng)常有香客問我,道長(zhǎng)盗胀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任锄贼,我火速辦了婚禮票灰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宅荤。我一直安慰自己屑迂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布冯键。 她就那樣靜靜地躺著惹盼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惫确。 梳的紋絲不亂的頭發(fā)上手报,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音改化,去河邊找鬼掩蛤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛陈肛,可吹牛的內(nèi)容都是我干的揍鸟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼句旱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼阳藻!你這毒婦竟也來了晰奖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤腥泥,失蹤者是張志新(化名)和其女友劉穎匾南,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體道川,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡午衰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冒萄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臊岸。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖尊流,靈堂內(nèi)的尸體忽然破棺而出帅戒,到底是詐尸還是另有隱情,我是刑警寧澤崖技,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布逻住,位于F島的核電站,受9級(jí)特大地震影響迎献,放射性物質(zhì)發(fā)生泄漏瞎访。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一吁恍、第九天 我趴在偏房一處隱蔽的房頂上張望扒秸。 院中可真熱鬧,春花似錦冀瓦、人聲如沸伴奥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拾徙。三九已至,卻和暖如春感局,著一層夾襖步出監(jiān)牢的瞬間尼啡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工蓝厌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玄叠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓拓提,卻偏偏與公主長(zhǎng)得像读恃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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