java.util系列源碼解讀之Timer定時(shí)器

Timer是jdk1.3中自帶的定時(shí)任務(wù)框架系統(tǒng).一個(gè)調(diào)度定時(shí)任務(wù)的工具線程類.可以執(zhí)行一個(gè)只調(diào)度一次的任務(wù)也可以重復(fù)調(diào)度一個(gè)一定間隔時(shí)間的任務(wù).
一個(gè)Timer實(shí)例就是一個(gè)調(diào)度任務(wù)調(diào)度線程.當(dāng)任務(wù)隊(duì)列中的所有定時(shí)任務(wù)被執(zhí)行完畢,這個(gè)定時(shí)調(diào)度的線程就會自動(dòng)終止.如果你想讓這個(gè)線程快速終止的話, 那么你可以直接調(diào)用cancel()方法可以讓調(diào)度線程快速終止.
Timer類是線程安全類:多個(gè)線程可以共享一個(gè)Timer實(shí)例.同時(shí)這個(gè)類不保證準(zhǔn)時(shí)調(diào)度任務(wù),因?yàn)樗怯玫腛bject.wait(long)方法.
Timer類能夠支持大量的并行調(diào)度任務(wù)(成百上千沒問題),在Timer內(nèi)部存儲每個(gè)調(diào)度任務(wù)的結(jié)構(gòu)是以平衡二叉樹堆(堆排序)的結(jié)構(gòu)來保存每個(gè)任務(wù)對象的,這種存儲結(jié)構(gòu)能在log(n)的時(shí)間復(fù)雜度內(nèi)快速的查詢.
jdk1.5中提供了比Timer跟高效的定時(shí)調(diào)度線程池類ScheduledThreadPoolExecutor


類定義

public class Timer {}

成員變量

修飾符 變量名 作用
private final TaskQueue queue = new TaskQueue() 任務(wù)隊(duì)列
private final TimerThread thread = new TimerThread(queue) 定時(shí)調(diào)度任務(wù)的線程類
private final Object threadReaper 用于終止定時(shí)調(diào)度線程的
private final static AtomicInteger nextSerialNumber = new AtomicInteger(0) 用于生成定時(shí)調(diào)度線程的名字

構(gòu)造方法

當(dāng)我們實(shí)例化一個(gè)Timer類之后, 定時(shí)調(diào)度的線程就會啟動(dòng)start()一直等待(queue.wait())任務(wù)隊(duì)列中加入任務(wù)

public Timer() {
    this("Timer-" + serialNumber());
}

public Timer(boolean isDaemon) {
    this("Timer-" + serialNumber(), isDaemon);
}

public Timer(String name) {
    thread.setName(name);
    thread.start();
}

public Timer(String name, boolean isDaemon) {
    thread.setName(name);
    thread.setDaemon(isDaemon);
    thread.start();
}

核心方法

Timer中有多個(gè)重載的public void schedule()方法,但是這些重載的方法只會做一些參數(shù)的判斷,并都最終調(diào)度的sched()方法,所以下面將講解sched()方法.

sched()方法

private void sched(TimerTask task, long time, long period) {
    if (time < 0)
        throw new IllegalArgumentException("Illegal execution time.");

    // Constrain value of period sufficiently to prevent numeric
    // overflow while still being effectively infinitely large.
    if (Math.abs(period) > (Long.MAX_VALUE >> 1))
        period >>= 1;
    
    // 獲取任務(wù)隊(duì)列的鎖(同一個(gè)線程多次獲取這個(gè)鎖并不會被阻塞,不同線程獲取時(shí)才可能被阻塞)
    synchronized(queue) {
        // 如果定時(shí)調(diào)度線程已經(jīng)終止了,則拋出異常結(jié)束
        if (!thread.newTasksMayBeScheduled)
            throw new IllegalStateException("Timer already cancelled.");
        
        // 再獲取定時(shí)任務(wù)對象的鎖(為什么還要再加這個(gè)鎖呢?想不清)
        synchronized(task.lock) {
            // 判斷線程的狀態(tài),防止多線程同時(shí)調(diào)度到一個(gè)任務(wù)時(shí)多次被加入任務(wù)隊(duì)列
            if (task.state != TimerTask.VIRGIN)
                throw new IllegalStateException(
                    "Task already scheduled or cancelled");
                    
            // 初始化定時(shí)任務(wù)的下次執(zhí)行時(shí)間
            task.nextExecutionTime = time;  
            // 重復(fù)執(zhí)行的間隔時(shí)間
            task.period = period;  
            // 將定時(shí)任務(wù)的狀態(tài)由TimerTask.VIRGIN(一個(gè)定時(shí)任務(wù)的初始化狀態(tài))設(shè)置為TimerTask.SCHEDULED
            task.state = TimerTask.SCHEDULED;  
        }
        
        // 將任務(wù)加入任務(wù)隊(duì)列
        queue.add(task);
        // 如果當(dāng)前加入的任務(wù)是需要第一個(gè)被執(zhí)行的(也就是他的下一次執(zhí)行時(shí)間離現(xiàn)在最近)
        // 則喚醒等待queue的線程(對應(yīng)到上面提到的queue.wait())
        if (queue.getMin() == task)
            queue.notify();
    }
}

cancel()終止定時(shí)線程

public void cancel() {
    // 從這里可以知道,如果調(diào)用了Timer的cancel()方法也不會立刻就終止定時(shí)調(diào)度線程
    // 因?yàn)檫@里需要獲取任務(wù)隊(duì)列的鎖,如果TimerThread占用了queue的鎖,也就是說queue并沒有在wait(),
    // 那么cancel就不會立刻終止定時(shí)線程, 他需要等待TimerThread定時(shí)線程釋放掉queue的鎖
    // 也就是說如果queue隊(duì)列中有定時(shí)任務(wù)存在,那么cancel就不會終止定時(shí)線程,他需要等到queue中的定時(shí)任務(wù)被清空
    // 用一句話說: cancel會等到所有定時(shí)任務(wù)執(zhí)行完后立刻終止定時(shí)線程
    synchronized(queue) {
        thread.newTasksMayBeScheduled = false;
        queue.clear();
        queue.notify();  // In case queue was already empty.
    }
}

purge()方法
從隊(duì)列中移除所有狀態(tài)為cancelled的任務(wù),調(diào)用這個(gè)方法并不會影響timer的行為,但是一般應(yīng)用不會調(diào)用這個(gè)方法,除非有很多被cancelled的任務(wù);同時(shí)調(diào)用這個(gè)方法也會比較消耗時(shí)間.


TimerThread類

TimerThread是Timer中定時(shí)調(diào)度線程類的定義,這個(gè)類會做為一個(gè)線程一直運(yùn)行來執(zhí)行Timer中任務(wù)隊(duì)列中的任務(wù).

類定義

class TimerThread extends Thread

成員變量

修飾符 變量名 作用
boolean newTasksMayBeScheduled = true 用于控制當(dāng)queue任務(wù)隊(duì)列為空時(shí),定時(shí)線程是否應(yīng)該立刻終止(false立刻終止)
private TaskQueue queue 任務(wù)隊(duì)列(這個(gè)當(dāng)TimerThread在Timer中被實(shí)例化時(shí)會傳入)

方法

run()方法
定時(shí)線程的執(zhí)行方法,它會調(diào)用TimerThread類的mainLoop()方法.

public void run() {
    try {
        mainLoop();
    } finally {
        // Someone killed this Thread, behave as if Timer cancelled
        synchronized(queue) {
            newTasksMayBeScheduled = false;
            queue.clear();  // Eliminate obsolete references
        }
    }
}

mainLoop()方法

private void mainLoop() {
    // 無限循環(huán)來控制等待任務(wù)隊(duì)列中加入任務(wù)
    while (true) {
        try {
            TimerTask task;
            boolean taskFired;
            // 獲取任務(wù)隊(duì)列的鎖
            synchronized(queue) {
                // 如果任務(wù)隊(duì)列為空,并且線程沒有被cancel()
                // 則線程等待queue鎖,queue.wait()方法會釋放獲得的queue鎖
                // 這樣在Timer中sched()方法才能夠獲取到queue鎖
                while (queue.isEmpty() && newTasksMayBeScheduled)
                    queue.wait();
                
                // 如果任務(wù)隊(duì)列為空了,那么就退出循環(huán)
                // 這種情況要發(fā)生,那么必須newTasksMayBeScheduled=false
                // 因?yàn)槿绻鹡ewTasksMayBeScheduled=true,就會在上面的while循環(huán)中執(zhí)行queue.wait(),使線程進(jìn)入等待狀態(tài)
                // 等線程從等待狀態(tài)恢復(fù)時(shí),說明queue.notify()方法被調(diào)用了,
                // 而觀察Timer代碼這只可能在sched()方法中發(fā)生, 這個(gè)方法會在隊(duì)列queue中add任務(wù)而使queue不再為空
                if (queue.isEmpty())
                    break; 

                long currentTime, executionTime;
                // 得到任務(wù)隊(duì)列中的位置1的任務(wù)
                task = queue.getMin();
                // 獲取任務(wù)的鎖
                synchronized(task.lock) {
                    // 如果任務(wù)被取消了(TimerTask.cancel()方法被調(diào)用)
                    // 將任務(wù)從隊(duì)列中移除,繼續(xù)重新循環(huán)
                    if (task.state == TimerTask.CANCELLED) {
                        queue.removeMin();
                        continue;  // No action required, poll queue again
                    }
                    
                    // 獲取任務(wù)的執(zhí)行時(shí)間
                    currentTime = System.currentTimeMillis();
                    executionTime = task.nextExecutionTime;
                    
                    // 計(jì)算任務(wù)是否應(yīng)該被觸發(fā)
                    if (taskFired = (executionTime<=currentTime)) {
                        // 任務(wù)應(yīng)該被觸發(fā),并且不是重復(fù)任務(wù)
                        // 將任務(wù)從隊(duì)列中移除并修改任務(wù)的執(zhí)行狀態(tài)
                        if (task.period == 0) { // Non-repeating, remove
                            queue.removeMin();
                            task.state = TimerTask.EXECUTED;
                        } else { // 任務(wù)是重復(fù)執(zhí)行任務(wù),計(jì)算任務(wù)下一次應(yīng)該被執(zhí)行的時(shí)間,并重新排序任務(wù)隊(duì)列
                            queue.rescheduleMin(
                              task.period<0 ? currentTime   - task.period
                                            : executionTime + task.period);
                        }
                    }
                }
                // 如果任務(wù)不應(yīng)被觸發(fā),讓其等待一定時(shí)間后執(zhí)行
                if (!taskFired) // Task hasn't yet fired; wait
                    queue.wait(executionTime - currentTime);
            }
            // 任務(wù)應(yīng)該被觸發(fā),讓任務(wù)執(zhí)行
            if (taskFired)  // Task fired; run it, holding no locks
                task.run();  // 任務(wù)也是一個(gè)線程
        } catch(InterruptedException e) {
        }
    }
}

TaskQueue類

TaskQueue是一個(gè)任務(wù)隊(duì)列類,用于保存定時(shí)器需要執(zhí)行的定時(shí)任務(wù),這個(gè)隊(duì)列是一個(gè)數(shù)組,只不過是一種平衡二叉樹堆結(jié)構(gòu)的數(shù)組.至于這個(gè)樹堆是怎么樣一種結(jié)構(gòu),還請執(zhí)行百度.只能說這種結(jié)構(gòu)總是保證值最小或者是值最大的在數(shù)組中的第一個(gè)位置(這個(gè)類中始終是nextExecuteTime最小的在第一個(gè)位置),沒當(dāng)隊(duì)列有增加,刪除操作就會重新調(diào)整隊(duì)列結(jié)構(gòu),讓nextExecuteTime值最小的放在第一個(gè)位置.


TimerTask類

TimerTask任務(wù)類,繼承自Thread,如果我們要用Timer來做定時(shí)任務(wù),那么我們必須繼承TimerTask類,并且實(shí)現(xiàn)run()方法(具體任務(wù)代碼).如果要取消一個(gè)任務(wù)的調(diào)度,則調(diào)用TimerTask.cancel()方法將取消任務(wù)的執(zhí)行.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赠涮,一起剝皮案震驚了整個(gè)濱河市盅安,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌世囊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窿祥,死亡現(xiàn)場離奇詭異株憾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晒衩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門嗤瞎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人听系,你說我怎么就攤上這事贝奇。” “怎么了靠胜?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵掉瞳,是天一觀的道長。 經(jīng)常有香客問我浪漠,道長陕习,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任址愿,我火速辦了婚禮该镣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘响谓。我一直安慰自己损合,他們只是感情好省艳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嫁审,像睡著了一般跋炕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上土居,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天枣购,我揣著相機(jī)與錄音,去河邊找鬼擦耀。 笑死棉圈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的眷蜓。 我是一名探鬼主播分瘾,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吁系!你這毒婦竟也來了德召?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤汽纤,失蹤者是張志新(化名)和其女友劉穎上岗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蕴坪,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肴掷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了背传。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呆瞻。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖径玖,靈堂內(nèi)的尸體忽然破棺而出痴脾,到底是詐尸還是另有隱情,我是刑警寧澤梳星,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布赞赖,位于F島的核電站,受9級特大地震影響冤灾,放射性物質(zhì)發(fā)生泄漏薯定。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一瞳购、第九天 我趴在偏房一處隱蔽的房頂上張望话侄。 院中可真熱鬧,春花似錦、人聲如沸年堆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽变丧。三九已至芽狗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痒蓬,已是汗流浹背童擎。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留攻晒,地道東北人顾复。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像鲁捏,于是被迫代替她去往敵國和親芯砸。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

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

  • Timer 定時(shí)器相信都不會陌生给梅,之所以拿它來做源碼分析假丧,是發(fā)現(xiàn)整個(gè)控制流程可以體現(xiàn)很多有意思的東西。 在業(yè)務(wù)開發(fā)...
    石先閱讀 6,331評論 2 13
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,522評論 25 707
  • 二叉樹是一種每個(gè)節(jié)點(diǎn)最好有左右兩個(gè)子節(jié)點(diǎn)的樹結(jié)構(gòu)动羽。概念的東西就不倒騰了包帚。意思意思就好。 算法之中运吓,對于二叉樹類的問...
    曲終人散Li閱讀 379評論 0 0
  • 生物上的性別區(qū)別規(guī)則婴噩,性細(xì)胞大的個(gè)體是雌性,性細(xì)胞小的個(gè)體是雄性(精子就比卵子小得多羽德。方便起見,就用精子和卵子來代...
    王麒宇閱讀 759評論 0 0
  • 132易水寒學(xué)經(jīng)匯報(bào) 2017年9月4日 星期四 寶貝年齡:M9歲10個(gè)月G3歲8個(gè)月 學(xué)習(xí)時(shí)間:305天 學(xué)習(xí)方...
    雅筑小易閱讀 269評論 0 0