Timer和TimerTask詳解
以下內(nèi)容根據(jù) The JavaTM Tutorial 和相關API doc翻譯整理,以供日后參考:
1.概覽
Timer是一種定時器工具,用來在一個后臺線程計劃執(zhí)行指定任務。它可以計劃執(zhí)行一個任務一次或反復多次。
TimerTask一個抽象類,它的子類代表一個可以被Timer計劃的任務。
簡單的一個例程:
- import java.util.Timer;
- import java.util.TimerTask;
- /**
- * Simple demo that uses java.util.Timer to schedule a task to execute
- * once 5 seconds have passed.
- */
- public class Reminder {
- Timer timer;
- public Reminder(int seconds) {
- timer = new Timer();
- timer.schedule(new RemindTask(), seconds*1000);
- }
- class RemindTask extends TimerTask {
- public void run() {
- System.out.println("Time's up!");
- timer.cancel(); //Terminate the timer thread
- }
- }
- public static void main(String args[]) {
- System.out.println("About to schedule task.");
- new Reminder(5);
- System.out.println("Task scheduled.");
- }
- }
運行這個小例子,你會首先看到:
About to schedule task.
5秒鐘之后你會看到:
Time's up!
這個小例子可以說明一些用Timer線程實現(xiàn)和計劃執(zhí)行一個任務的基礎步驟:
- 實現(xiàn)自定義的TimerTask的子類,run方法包含要執(zhí)行的任務代碼,在這個例子里,這個子類就是RemindTask。
- 實例化Timer類,創(chuàng)建計時器后臺線程。
- 實例化任務對象 (new RemindTask()).
- 制定執(zhí)行計劃。這里用schedule方法,***個參數(shù)是TimerTask對象,第二個參數(shù)表示開始執(zhí)行前的延時時間(單位是milliseconds,這里定義了5000)。還有一種方法可以指定任務的執(zhí)行時間,如下例,指定任務在11:01 p.m.執(zhí)行:
- //Get the Date corresponding to 11:01:00 pm today.
- Calendar calendar = Calendar.getInstance();
- calendar.set(Calendar.HOUR_OF_DAY, 23);
- calendar.set(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- Date time = calendar.getTime();
- timer = new Timer();
- timer.schedule(new RemindTask(), time);
2.終止Timer線程
默認情況下,只要一個程序的timer線程在運行,那么這個程序就會保持運行。當然,你可以通過以下四種方法終止一個timer線程:
調(diào)用timer的cancle方法。你可以從程序的任何地方調(diào)用此方法,甚至在一個timer task的run方法里。
讓timer線程成為一個daemon線程(可以在創(chuàng)建timer時使用new Timer(true)達到這個目地),這樣當程序只有daemon線程的時候,它就會自動終止運行。
當timer相關的所有task執(zhí)行完畢以后,刪除所有此timer對象的引用(置成null),這樣timer線程也會終止。
調(diào)用System.exit方法,使整個程序(所有線程)終止。
Reminder的例子使用了***種方式。在這里不能使用第二種方式,因為這里需要程序保持運行直到timer的任務執(zhí)行完成,如果設成daemon,那么當main線程結(jié)束的時候,程序只剩下timer這個daemon線程,于是程序不會等timer線程執(zhí)行task就終止了。
有些時候,程序的終止與否并不只與timer線程有關。舉個例子,如果我們使用AWT來beep,那么AWT會自動創(chuàng)建一個非daemon線程來保持程序的運行。
- import java.util.Timer;
- import java.util.TimerTask;
- import java.awt.Toolkit;
- /**
- * Simple demo that uses java.util.Timer to schedule a task to execute
- * once 5 seconds have passed.
- */
- public class ReminderBeep {
- Toolkit toolkit;
- Timer timer;
- public ReminderBeep(int seconds) {
- toolkit = Toolkit.getDefaultToolkit();
- timer = new Timer();
- timer.schedule(new RemindTask(), seconds*1000);
- }
- class RemindTask extends TimerTask {
- public void run() {
- System.out.println("Time's up!");
- toolkit.beep();
- //timer.cancel(); //Not necessary because we call System.exit
- System.exit(0); //Stops the AWT thread (and everything else)
- }
- }
- public static void main(String args[]) {
- System.out.println("About to schedule task.");
- new ReminderBeep(5);
- System.out.println("Task scheduled.");
- }
- }
3.反復執(zhí)行一個任務
先看一個例子:
- public class AnnoyingBeep {
- Toolkit toolkit;
- Timer timer;
- public AnnoyingBeep() {
- toolkit = Toolkit.getDefaultToolkit();
- timer = new Timer();
- timer.schedule(new RemindTask(),
- 0, //initial delay
- 1*1000); //subsequent rate
- }
- class RemindTask extends TimerTask {
- int numWarningBeeps = 3;
- public void run() {
- if (numWarningBeeps > 0) {
- toolkit.beep();
- System.out.println("Beep!");
- numWarningBeeps--;
- } else {
- toolkit.beep();
- System.out.println("Time's up!");
- //timer.cancel(); //Not necessary because we call System.exit
- System.exit(0); //Stops the AWT thread (and everything else)
- }
- }
- }
- ...
- }
執(zhí)行,你會看到如下輸出:
- Task scheduled.
- Beep!
- Beep! //one second after the first beep
- Beep! //one second after the second beep
- Time's up! //one second after the third beep
這里使用了三個參數(shù)的schedule方法用來指定task每隔一秒執(zhí)行一次。如下所列為所有Timer類用來制定計劃反復執(zhí)行task的方法 :
- schedule(TimerTask task, long delay, long period)
- schedule(TimerTask task, Date time, long period)
- scheduleAtFixedRate(TimerTask task, long delay, long period)
- scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
當計劃反復執(zhí)行的任務時,如果你注重任務執(zhí)行的平滑度,那么請使用schedule方法,如果你在乎的是任務的執(zhí)行頻度那么使用scheduleAtFixedRate方法。 例如,這里使用了schedule方法,這就意味著所有beep之間的時間間隔至少為1秒,也就是說,如果有一個beap因為某種原因遲到了(未按計劃執(zhí)行),那么余下的所有beep都要延時執(zhí)行。如果我們想讓這個程序正好在3秒以后終止,無論哪一個beep因為什么原因被延時,那么我們需要使用scheduleAtFixedRate方法,這樣當***個beep遲到時,那么后面的beep就會以最快的速度緊密執(zhí)行(***限度的壓縮間隔時間)。
4.進一步分析schedule和scheduleAtFixedRate
(1)2個參數(shù)的schedule在制定任務計劃時, 如果指定的計劃執(zhí)行時間scheduledExecutionTime<=systemCurrentTime,則task會被立即執(zhí)行。scheduledExecutionTime不會因為某一個task的過度執(zhí)行而改變。
(2)3個參數(shù)的schedule在制定反復執(zhí)行一個task的計劃時,每一次執(zhí)行這個task的計劃執(zhí)行時間隨著前一次的實際執(zhí)行時間而變,也就是scheduledExecutionTime(第n+1次)=realExecutionTime(第n次)+periodTime。也就是說如果第n次執(zhí)行task時,由于某種原因這次執(zhí)行時間過長,執(zhí)行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),則此時不做時隔等待,立即執(zhí)行第n+1次task,而接下來的第n+2次task的scheduledExecutionTime(第n+2次)就隨著變成了realExecutionTime(第n+1次)+periodTime。說白了,這個方法更注重保持間隔時間的穩(wěn)定。
(3)3個參數(shù)的scheduleAtFixedRate在制定反復執(zhí)行一個task的計劃時,每一次執(zhí)行這個task的計劃執(zhí)行時間在最初就被定下來了,也就是scheduledExecutionTime(第n次)=firstExecuteTime+n*periodTime;如果第n次執(zhí)行task時,由于某種原因這次執(zhí)行時間過長,執(zhí)行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),則此時不做period間隔等待,立即執(zhí)行第n+1次task,而接下來的第n+2次的task的scheduledExecutionTime(第n+2次)依然還是firstExecuteTime+(n+2)*periodTime這在***次執(zhí)行task就定下來了。說白了,這個方法更注重保持執(zhí)行頻率的穩(wěn)定。
5.一些注意的問題
- 每一個Timer僅對應唯一一個線程。
- Timer不保證任務執(zhí)行的十分精確。
- Timer類的線程安全的。
原文鏈接:http://blog.csdn.net/xiaozhang0731/article/details/5688345
【編輯推薦】