Java線程和多線程(十)——TimerTask

Java中的java.util.Timer是一個工具類,可以用於調度一個線程在將來的某一個時刻執行特定的任務。Java Timer類可以將一個任務定時執行一次,或者是以後以每隔一定的時間間隔來觸發一次。

Java TimerTask

java.util.TimerTask是一個抽象類,也同時實現了Runnable接口的。我們可以繼承這個類來創建我們自己的TimerTask之後由Timer來調度。

Java Timer舉例

Java Timer類是線程安全的,多個線程可以共享一個Timer對象而不需要額外的同步操作。Timer類會使用java.util.TaskQueue來將Task以一定的時間間隔加入到隊列之中,而且,在任何時候僅僅能有一個線程在運行TimerTask。舉例來說,開發者創建了一個Timer每隔10秒來執行一次,但是單線程的執行需要使用20秒,那麼Timer對象會持續將任務添加到隊列中,只要線程的執行完成了,它會通知隊列中另一個線程來立刻執行。

Java Timer類通過對象的waitnotify方法來調度任務。

參見如下代碼:

package com.sapphire.threads;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class MyTimerTask extends TimerTask {

    @Override
    public void run() {
        System.out.println("Timer task started at:"+new Date());
        completeTask();
        System.out.println("Timer task finished at:"+new Date());
    }

    private void completeTask() {
        try {
            //assuming it takes 20 secs to complete the task
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]){
        TimerTask timerTask = new MyTimerTask();
        //running timer task as daemon thread
        Timer timer = new Timer(true);
        timer.scheduleAtFixedRate(timerTask, 0, 10*1000);
        System.out.println("TimerTask started");
        //cancel after sometime
        try {
            Thread.sleep(120000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timer.cancel();
        System.out.println("TimerTask cancelled");
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

需要注意的是,上面的一個線程的執行會花掉20秒,但是Java的Timer對象調度的執行間隔是10秒執行一次,所以輸出如下:

TimerTask started
Timer task started at:Fri Oct 14 12:59:27 CST 2016
Timer task finished at:Fri Oct 14 12:59:47 CST 2016
Timer task started at:Fri Oct 14 12:59:47 CST 2016
Timer task finished at:Fri Oct 14 13:00:07 CST 2016
Timer task started at:Fri Oct 14 13:00:07 CST 2016
Timer task finished at:Fri Oct 14 13:00:27 CST 2016
Timer task started at:Fri Oct 14 13:00:27 CST 2016
Timer task finished at:Fri Oct 14 13:00:47 CST 2016
Timer task started at:Fri Oct 14 13:00:47 CST 2016
Timer task finished at:Fri Oct 14 13:01:07 CST 2016
Timer task started at:Fri Oct 14 13:01:07 CST 2016
TimerTask cancelled
Timer task finished at:Fri Oct 14 13:01:27 CST 2016

輸出也確認了一點,就是如果一個任務已經開始執行了,那麼Timer會等待它的完成,而一旦完成,Timer會立刻開始執行隊列中的下一個任務。

Java的對象可以以守護線程的方式來調度相關的任務。Timer的cancel()方法是用來結束Timer並拋棄其他的調度任務的。然而,這個方法不會影響現在正在執行的任務,而是讓其繼續運行下去。如果我們將Timer的運行配置爲守護線程,那麼無論我們是否調用cancel()方法,一旦用戶線程完成執行,Timer都會立刻結束掉。

Timer類中包含多個schedule()方法,可以調度任務在指定時間執行一次,或者在一些延遲後來執行。Timer類中也包含了一些scheduleAtFixedRate()方法來以指定的時間間隔來運行一些任務。

注意:當使用Timer來進行任務調度的時候,開發者需要確認無時間間隔一定要大於線程的執行時間。否則,任務的隊列會變得越來越長而實際的任務也會總是在執行。

前面的描述也相當於隱含着提到了Timer的可能的錯誤行爲,那就是Timer執行任務的方式。Timer的實現是內部包含一個隊列,和一個線程,然後來定時將任務添加到執行隊列之中的。

public class Timer {
    private final TaskQueue queue = new TaskQueue();
    private final TimerThread thread = new TimerThread(queue);
}

這樣就可能出現問題,如果將2個TimerTask加入到Timer中進行執行,如果其中一個Timer執行的比較忙,而另一個需要執行的又比較快,可能就會出現問題。舉例來說,兩個任務,一個每隔300秒執行一次,一個每隔5秒執行一次,如果第一個任務執行了20秒才結束,那麼因爲上面實現的原因,會將第二個任務阻塞20秒,而一口氣執行四次。所以開發者在使用Timer來進行任務調度的時候,需要考慮性能以及可能出現的錯誤行爲。

發佈了44 篇原創文章 · 獲贊 24 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章