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類通過對象的wait
和notify
方法來調度任務。
參見如下代碼:
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
來進行任務調度的時候,需要考慮性能以及可能出現的錯誤行爲。