Timer源碼之TimerThread

      Timer只是管理着一個TimerTask任務隊列(queue)而TimerTask的真正執行是由TimerThread來執行的,TimerThread繼承了Thread,它的主要工做就是不斷從優先級隊列queue中取出任務檢查是否到了能夠執行的時間,若是能夠執行就調用TimerTask的run方法。java

     很是值得注意的是任務隊列(queue)中的TimerTask雖然實現了Runnable接口,可是在TimerThread中並無啓動新的線程來執行它,只有Thread的start方法才能啓動新的線程。因此若是TimerTask中的run方法是死循環,那麼可能Timer的任務隊列中的TimerTask永遠得不到執行的機會。仍是先看一下TimerThread的代碼吧。oop

import java.util.TimerTask;

public class TimerThread extends Thread {
 
    boolean newTasksMayBeScheduled = true;

    /**
     * 任務隊列的引用
     */
    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            mainLoop();
        } finally {
            // 任務結束,例如Timer 執行了cancel
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  //清除廢棄對象的引用
            }
        }
    }

    /**
     * 
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // 若是暫時沒有任務,而且TimerThread線程沒有被取消就等待
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // 若是任務隊列爲空而且TimerThread已經被取消了,直接結束

                    long currentTime, executionTime;
                    //咱們已經知道queue隊列是一個按下一次執行時間排序的優先隊列
                    //因此,這裏是獲取到執行時間最近的TimerTask
                    task = queue.getMin();
                    
                    //主要防止TimerTask的state域被其餘線程修改,而形成數據不一致
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;//下一次執行的時間
                        //若是下一次執行時間小於等於當前時間說明能夠執行了
                        if (taskFired = (executionTime<=currentTime)) {
                            // period等於0說明不是週期執行的,就直接移除並把TimerTask的狀態修改成執行過了
                            if (task.period == 0) {
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } 
                            // 這裏比較巧妙,從新計算下一次的執行時間,咱們知道period<0是按固定的時間間隔執行
                            //看下面的代碼當period<0的時候下一次的執行時間(注意減的是負數)
                            //nextExecutionTime = currentTime - period,因此每一次計劃執行完以後的period再執行
                            //period>0是按固定的頻率執行,顯然在executionTime<=currentTime條件下同
                            //executionTime + task.period計算一下次執行時間就儘可能保證了在每兩次計劃執行期間的period
                            //儘可能相等,由於executionTime每一次都是計算出來的是固定的,因此它能保證頻率固定
                            else { 
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    // 任務尚未被移除,說明最小的執行時間都尚未到,直接等待
                    if (!taskFired) 
                        queue.wait(executionTime - currentTime);
                }
                // 若是任務(TimerTask)已經從隊列中移除,說明已經到了執行時間,能夠執行
                if (taskFired)  
                    task.run();//只是調用的run方法沒有啓用新的線程
            } catch(InterruptedException e) {
            }
        }
    }
}

在mainLoop中有這樣一句代碼:this

queue.rescheduleMin(task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);

      TimerTask中以一個nextExecutionTime字段是表示下一次的執行時間的,上面的語句就是從新設置隊列優先級最高(堆頂)元素的下一次執行時間的語句。當period<0的時候currentTime-period就是加上period絕對值。currentTime是當前的時間是變化的,period是固定的,因此固定延遲(fixed-delay)就好理解了,就是無論TimerTask執行了多久,從新安排執行計劃的時候卻是按當前時間向後延遲period這麼長的時間。線程

      當period>0的時候表示固定頻率(fixed-rate)執行,executionTime就是TimerTask的 nextExecutionTime , 當period>0的時候 nextExecutionTime 每一次都是有上一次的 nextExecutionTime +period計算來的,它兩次執行區間可能就不須要等period這麼長時間了。例如:沒有延遲當即開始執行,每一次執行2分鐘,period爲3。使用固定延遲就是0分鐘執行第一次,執行了2分鐘而後下一次執行時間就是2+3=5,而後在執行2分鐘第三次執行時間就是7+3=10分鐘的時候。而使用固定頻率的方式就是第一次執行是在0分鐘,下一次執行時間是在0+3=3分鐘的時候執行,而後他就會在第3分鐘執行,第二次是在3+3=6分鐘的時候執行,它和執行的時間基本沒有關係,就是一個等差數列。code

       由於Timer主要對外提供的接口就是schedule方法,因此Timer自己沒有太多的內容,可是它有一個很是好技巧,就是他對外提供的不一樣參數的固定頻率執行,固定延遲執行不少接口,可是它的內部實現只是巧妙的經過參數的變換把它抽象都了一個方法中。有時候以爲別人的類寫的特別好用從這裏就能夠看出一點,不要讓使用接口的人來控制變化來作不一樣的實現,不多有人願意記住,而是經過直觀的方法名來對外提供接口,咱們內部實現來控制變化。看Timer提供的的各類schedule接口基本都是經過period等參數變化實現的。而咱們看到的就是直觀的schedule方法。
 對象

相關文章
相關標籤/搜索