定時任務(wù)之Timer

Timer百框,和handler的post機(jī)制類似尖阔。都可以用于之后某個時刻執(zhí)行任務(wù)的實現(xiàn)浸锨,Timer恒存在自己的子線程,任務(wù)也在該子線程中執(zhí)行璧亚;handler.post出去的任務(wù)在什么線程執(zhí)行取決于其Looper所在線程讨韭。

一、簡介

1.1?Timer原理簡述

Timer是一個用于執(zhí)行定時任務(wù)的類癣蟋,可以單次執(zhí)行或按指定時間間隔循環(huán)執(zhí)行(直到主動cancel或線程被殺掉)透硝。Timer中任務(wù)處理采用了生產(chǎn)者-消費者模型的設(shè)計思想。具體知識點包含以下幾點:

1)一個存儲任務(wù)的任務(wù)池TaskQueue梢薪,包含一個初始大小為128的TimerTask數(shù)組蹬铺,負(fù)責(zé)任務(wù)的存儲(add)、排序(fixUp秉撇、fixDown)甜攀、取出(getMin)、清理(removeMin琐馆、quickRemove)规阀、循環(huán)任務(wù)處理(rescheduleMin)以及一些其他基本操作。并通過排序保證隊頭任務(wù)的執(zhí)行一定是最早的瘦麸。

2)一個作為事件消費者的TimerThread谁撼,TimerThread中不斷獲取當(dāng)前任務(wù)隊列的隊頭任務(wù),執(zhí)行任務(wù)滋饲。并根據(jù)任務(wù)是否需要循環(huán)決定是移除任務(wù)還是將任務(wù)按下一次執(zhí)行時間重新加入到任務(wù)隊列中厉碟。在TimerThread中不斷獲取待執(zhí)行任務(wù)時,采用了Object.wait()和Object.notify()的機(jī)制屠缭。Object.wait()保證了任務(wù)隊列為空時及時釋放資源箍鼓,而當(dāng)有新的任務(wù)時也通過Object.notify()及時恢復(fù)任務(wù)的遍歷。

3)TimerTask是任務(wù)實體呵曹,Runnable接口的實現(xiàn)類款咖。內(nèi)部包含用于線程安全的鎖lock、用于標(biāo)記任務(wù)狀態(tài)的字段state奄喂、以及一個供用戶實現(xiàn)的任務(wù)內(nèi)容抽象方法run().

4)Timer本身提供了對以上三者操作的封裝铐殃、實例化和對外暴露運行任務(wù)的接口。同時跨新,作為生產(chǎn)者富腊,將用戶任務(wù)加入到任務(wù)隊列;對消費者層面玻蝌,Timer也是和消費者線程唯一綁定的蟹肘,負(fù)責(zé)啟動消費者線程词疼,并在生產(chǎn)了新的任務(wù)后及時通知已經(jīng)休眠的消費者。提供了多種構(gòu)造方法和清理接口帘腹。

Timer是一個控制中心贰盗,對外和用戶交互,接收用戶要定時執(zhí)行的任務(wù)阳欲;對內(nèi)既是生產(chǎn)者又是消費者舵盈,控制 任務(wù)的存儲、調(diào)度球化、執(zhí)行的發(fā)起者秽晚。具體到TimerThread、TimerTask其實是一種委托思想(類似android中事件機(jī)制)筒愚,Timer委托二者一個作為生產(chǎn)者赴蝇、一個作為消費者。Timer不斷將新任務(wù)按序交給生產(chǎn)者保存巢掺,同時委托消費者線程不斷消費生產(chǎn)者隊列中待執(zhí)行的任務(wù)句伶,并根據(jù)是否循環(huán)進(jìn)行調(diào)度。

對于取消操作陆淀,消費者線程的根據(jù)newTasksMayBeScheduled字段和消費隊列是否為空雙重保障考余。而任務(wù)本身通過資深的狀態(tài)字段進(jìn)行控制。

1.2?Timer和Handler(或view)的post/postDelay

Timer和Handler.post都可以執(zhí)行延時任務(wù)轧苫。

二者的相同之處如下:

1)執(zhí)行的任務(wù)均為Runnable的實現(xiàn)類楚堤。

2)均可以指定延時時長delay。

3)二者皆通過生產(chǎn)者-消費者模型實現(xiàn)含懊。

二者的不同之處:

1)Timer的任務(wù)是在新開的子線程中執(zhí)行的身冬,所以不可以做更新UI的操作,但可以做耗時任務(wù)(對于存在多任務(wù)的case岔乔,不建議又耗時任務(wù)吏恭,因為耗時任務(wù)會影響后續(xù)任務(wù)的執(zhí)行時間);Handler.post方式執(zhí)行的任務(wù)是在Looper對應(yīng)線程中執(zhí)行的重罪,能否更新UI及做耗時任務(wù)取決于Looper對應(yīng)線程是否是主線程。

2)Handler.post的方式執(zhí)行任務(wù)只能執(zhí)行一次(可通過遞歸調(diào)用哀九、自調(diào)用的方式實現(xiàn)repeat)剿配;Timer封裝好了相關(guān)接口,并提供了兩種不同的循環(huán)方式阅束。

3)從代碼層面呼胚,Looper既是生產(chǎn)者(容器)也是消費者。并采用ThreadLocal機(jī)制將自身和線程一一綁定息裸,也就有了任務(wù)執(zhí)行取決于Looper的產(chǎn)生蝇更。具體下一篇再分析沪编。

4)handler中提供了handleMessage方法,是另一種消息分發(fā)年扩∫侠可以和以上兩種機(jī)制結(jié)合使用,用來切線程厨幻。

二相嵌、源碼分析

2.1 任務(wù)池TaskQueue

在其中存在一個初始大小128的array,根據(jù)注釋可以知道這是一個用做一個平衡二叉樹的模型况脆,一個父節(jié)點array[n]下掛載的兩個子節(jié)點為array[2n]和array[2n+1].

2.1.1 任務(wù)實體TimerTask

TimerTask是實現(xiàn)了Runnable的抽象類饭宾。

lock用于鎖實現(xiàn),為外邊獲取來進(jìn)行線程同步格了;state值用于標(biāo)記任務(wù)狀態(tài)看铆。nextExecutionTime是任務(wù)的下次執(zhí)行時間,在執(zhí)行時用該字段和當(dāng)前時間比較決定是否到了執(zhí)行的時候盛末。使用地方可以看如下代碼:


這段timer中的代碼是任務(wù)的調(diào)用處理弹惦,首先從任務(wù)隊列里邊取出隊頭(即最先被執(zhí)行的任務(wù)),獲取任務(wù)的鎖满败,判斷任務(wù)狀態(tài)是否被置為cancelled肤频,如果是則移除該任務(wù)再遍歷下一個。如果沒有則比較當(dāng)前時間和任務(wù)應(yīng)執(zhí)行時間算墨,如果應(yīng)該執(zhí)行宵荒,則判斷是不是循環(huán)任務(wù),如果是則將任務(wù)按下一次執(zhí)行時間加入到任務(wù)池中净嘀。

二叉樹的插入和刪除邏輯為


在插入任務(wù)時报咳,首先判斷當(dāng)前隊列是否能裝下,不能的話會以queue.length*2的方式擴(kuò)展挖藏。之后調(diào)整位置暑刃。
在刪除隊頭的時候,把隊頭指向隊尾元素膜眠,同時調(diào)整隊頭的位置岩臣。
fixup和fixdown用于調(diào)整位置。以nextExecutionTime從小到大宵膨,二叉樹從上向下的方式排列架谎,且二叉樹的父節(jié)點和兩個子節(jié)點的下標(biāo)為n和2n/2n+1.具體算法請看平衡二叉樹算法,不展開詳解辟躏。

2.2 任務(wù)的消費者線程TimerThread


以上是消費者線程谷扣,在其中當(dāng)任務(wù)池中為空且會再執(zhí)行時,會wait()捎琐,直到隊列中有新任務(wù)時notify()喚醒会涎,如下:


這是timer主體中外界添加新任務(wù)時裹匙,如果add的任務(wù)正好是要執(zhí)行的(證明在add該任務(wù)之前,任務(wù)隊列中無可執(zhí)行任務(wù)末秃,也即線程已經(jīng)wait())概页,則通過notify喚醒。

這樣消費者線程在任務(wù)隊列有任務(wù)時蛔溃,通過循環(huán)不斷執(zhí)行任務(wù)绰沥,無任務(wù)時,若想讓線程停止退出則要通過改變線程字段的方式贺待。如下:


在循環(huán)中徽曲,

1、隊列為空且newTasksMayBeScheduled成立麸塞,則wait等待

2秃臣、1不成立,且隊列為空(即newTasksMayBeScheduled不成立)哪工,則跳出循環(huán)奥此,線程執(zhí)行完畢跳出。再看run方法中finally中為清理工作雁比。


2.3 timer對外接口


對于用戶調(diào)用稚虎,timer提供了兩組方法,schedule和scheduleAtFixedRate偎捎,這兩組方法又都會調(diào)用到sched方法蠢终,注意此時的第三個參數(shù),period和-period茴她,正數(shù)和負(fù)數(shù)的差別寻拂。最終影響為第三張圖。

結(jié)論:當(dāng)period<0時丈牢,不論任務(wù)執(zhí)行時間是否延誤祭钉,下一次都在固定時間執(zhí)行,參照系為當(dāng)前時間

當(dāng)period>0時己沛,任務(wù)在下一次時間后一定時間執(zhí)行慌核,參照系為下一次具體執(zhí)行時間。

三 使用方法


使用方法很簡單申尼,調(diào)用對應(yīng)方法即可遂铡。

最后有幾點需要注意:

1)task是一定執(zhí)行在timer子線程中(不論調(diào)用線程是否為主線程),所以若需要在任務(wù)中執(zhí)行更新ui的操作晶姊,可以通過runonuithread或顯示通過handler方式切回主線程。

2)在不使用時一定要及時cancel清理伪货,釋放資源们衙。

3)當(dāng)timer中有多任務(wù)時钾怔,因為后邊任務(wù)會依賴前邊任務(wù)執(zhí)行完,尤其是如果有耗時任務(wù)蒙挑,會發(fā)生定時不準(zhǔn)確的現(xiàn)象宗侦。

4)當(dāng)存在多任務(wù)時,若其中某個因異常而終止忆蚀,則會退出所有任務(wù)的執(zhí)行(消費者線程被異常終止了)


頭一次寫東西矾利,用來記錄自己的所學(xué)所獲,文筆不好馋袜,理解可能也有所片面男旗,希望看到的朋友不吝賜教,共同進(jìn)步欣鳖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末察皇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泽台,更是在濱河造成了極大的恐慌什荣,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怀酷,死亡現(xiàn)場離奇詭異稻爬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蜕依,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門桅锄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笔横,你說我怎么就攤上這事竞滓。” “怎么了吹缔?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵商佑,是天一觀的道長。 經(jīng)常有香客問我厢塘,道長茶没,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任晚碾,我火速辦了婚禮抓半,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘格嘁。我一直安慰自己笛求,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著探入,像睡著了一般狡孔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜂嗽,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天苗膝,我揣著相機(jī)與錄音,去河邊找鬼植旧。 笑死辱揭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的病附。 我是一名探鬼主播问窃,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼胖喳!你這毒婦竟也來了泡躯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤丽焊,失蹤者是張志新(化名)和其女友劉穎较剃,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年琼富,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啊送。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖欣孤,靈堂內(nèi)的尸體忽然破棺而出馋没,到底是詐尸還是另有隱情,我是刑警寧澤降传,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布篷朵,位于F島的核電站,受9級特大地震影響婆排,放射性物質(zhì)發(fā)生泄漏声旺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一段只、第九天 我趴在偏房一處隱蔽的房頂上張望腮猖。 院中可真熱鬧,春花似錦赞枕、人聲如沸澈缺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姐赡。三九已至误堡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雏吭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工陪踩, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留杖们,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓肩狂,卻偏偏與公主長得像摘完,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子傻谁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

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

  • 美圖欣賞 Java孝治、Android知識點匯集 Java集合類 ** Java集合相關(guān)的博客** java面試相關(guān) ...
    ElvenShi閱讀 1,753評論 0 2
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 8,202評論 8 57
  • 第5章 多線程編程 5.1 線程基礎(chǔ) 5.1.1 如何創(chuàng)建線程 在java要創(chuàng)建線程,一般有==兩種方式==:1)...
    AndroidMaster閱讀 1,796評論 0 11
  • 用兩張圖告訴你审磁,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料谈飒? 從這篇文章中你...
    hw1212閱讀 12,754評論 2 59
  • 11.05 為什么我會突然想哭 有時候我覺得幸福會蜷縮 因為有些聲響太刺耳、尖銳 說不出那是一種什么感覺 恍若态蒂,在...
    佐恬閱讀 216評論 0 0