jdk定時任務的原理

先來看下timer的調用方式,簡單的定時打印java

public static void main(String[] args){
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		}, 5000, 5000);
	}

運行結果:node

Timer-0
Timer-0
Timer-0
Timer-0
Timer-0多線程

而後我好奇它是如何實現定時調用的,打開TimerTask類的源碼,發現它是一個對runable的封裝,添加了一些狀態和同步鎖對象以及下次執行的時間,它封裝瞭如下狀態ide

 /**
     * This task has not yet been scheduled.任務尚未被調用
     */
    static final int VIRGIN = 0;oop

    /**
     * This task is scheduled for execution.  If it is a non-repeating task,
     * it has not yet been executed. 任務被調度去執行,若是是一個不循環執行的任務,表示它尚未被執行
     */
    static final int SCHEDULED   = 1;ui

    /**
     * This non-repeating task has already executed (or is currently
     * executing) and has not been cancelled.不重複執行任務已經執行或者正在執行,不能被取消
     */
    static final int EXECUTED    = 2;this

    /**
     * This task has been cancelled (with a call to TimerTask.cancel).任務已經被取消,(調用 timerTask 的cancel 方法
     */
    static final int CANCELLED   = 3;線程

 /**code

    * 下次執行的時間,System.currentTimeMillis 格式
     * Next execution time for this task in the format returned by
     * System.currentTimeMillis, assuming this task is scheduled for execution.
     * For repeating tasks, this field is updated prior to each task execution.
     */
    long nextExecutionTime;orm

    /**

    * 執行間隔
     * Period in milliseconds for repeating tasks.  A positive value indicates
     * fixed-rate execution.  A negative value indicates fixed-delay execution.
     * A value of 0 indicates a non-repeating task.
     */
    long period = 0;

     

   以上能夠看出timer+timerTask的方式不適合使用天然時間的任務。

 

    Timer.class

   timer主要有兩個關鍵的成員變量final修飾

    //內部類實現,timerTask的隊列,封裝實現了,最大128個任務

    private final TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.,內部類,一個線程的封裝實現
     */
    private final TimerThread thread = new TimerThread(queue);

 

   先簡單介紹下TaskQueue.class,當作員變量

/**

    保存定時任務隊列,能夠看到最大一個timer最多能夠提交128個任務
     * Priority queue represented as a balanced binary heap: the two children
     * of queue[n] are queue[2*n] and queue[2*n+1].  The priority queue is
     * ordered on the nextExecutionTime field: The TimerTask with the lowest
     * nextExecutionTime is in queue[1] (assuming the queue is nonempty).  For
     * each node n in the heap, and each descendant of n, d,
     * n.nextExecutionTime <= d.nextExecutionTime.
     */
    private TimerTask[] queue = new TimerTask[128];

    /**
     * The number of tasks in the priority queue.  (The tasks are stored in
     * queue[1] up to queue[size]).
     */
    private int size = 0;

  TaskQueue提供了一系列方法是對該隊列即將執行的任務的操做,獲取最近要執行的人,修改任務的下次執行時間

   主要是有兩個私有方法fixUp(),fixDown();用來更新隊列的順序。

 

    再來看下TimerThread.class,主要看下run方法

public void run() {
        try {           
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
//遇到異常或取消任務,清空任務隊列,修改狀態,不按照能夠提交新的任務
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

    /**
     * The main timer loop.  (See class comment.)
     */
    private void mainLoop() {
//使用while(true)實現線程一直在運行,而不是任務到時間了另起一個線程,因此timer只有一個線程
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        //new timer()的時候線程已經啓動了的,在這裏等帶任務的提交,爲何是用while呢?
                        queue.wait();
                    if (queue.isEmpty())//隊列空直接退出,在中止timer的狀況下
                        break; // Queue is empty and will forever remain; die

                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    task = queue.getMin();//獲取最近要執行
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {//取消狀態的任務,從隊列中刪除,並不往下執行
                            queue.removeMin();
                            continue;  // No action required, poll queue again
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        if (taskFired = (executionTime<=currentTime)) {
                             //若是任務下次執行時間小於等於當前時間,即該任務能夠執行
                            if (task.period == 0) { // Non-repeating, remove
                                queue.removeMin();//非循環任務,從隊列中刪除,並修改任務狀態
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                //循環任務,須要從新構建隊列排序
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait//最近的任務還沒到時間,在等待
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();//執行任務
            } catch(InterruptedException e) {
            }
        }
    }
}

能夠看到timer的定時實際上是使用一個線程while(true)和,wait(),notify()配合實現的。代碼有註釋很少解釋了。

 

總結:

jdk timer + timertask的方式實現的定時任務,因爲實現方式的侷限性,致使不能支持更多的特性,多線程,天然時間等。 

相關文章
相關標籤/搜索