java之定時器Timer
1 概覽
Timer
是一種定時器工具,用來在一個後臺執行緒計劃執行指定任務。它可以計劃執行一個任務一次或反覆多次。
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
執行緒實現和計劃執行一個任務的基礎步驟:
實現自定義的TimerTask
的子類,run
方法包含要執行的任務程式碼,在這個例子裡,這個子類就是RemindTask
例項化Timer
類,建立計時器後臺執行緒。
例項化任務物件 (new RemindTask())
,制定執行計劃。
這裡用schedule
方法,第一個引數是TimerTask
物件,第二個引數表示開始執行前的延時時間(單位是milliseconds
,這裡定義了5000
)。
還有一種方法可以指定任務的執行時間,如下例,指定任務在11:01 p.m
執行:
//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
執行緒:
- timer.cancle()方法
呼叫timer
的cancle
方法。可以從程式的任何地方呼叫此方法,甚至在一個timer task
的run
方法裡 - 成為daemon執行緒
讓timer
執行緒成為一個daemon
執行緒(可以在建立timer
時使用new Timer(true)
達到這個目地),這樣當程式只有daemon
執行緒的時候,它就會自動終止執行。 - 刪除timer物件引用
當timer
相關的所有task
執行完畢以後,刪除所有此timer
物件的引用(置成null
timer
執行緒也會終止 - System.exit方法
呼叫System.exit
方法,使整個程式(所有執行緒)終止。
Reminder
的例子使用了第一種方式。在這裡不能使用第二種方式,因為這裡需要程式保持執行直到timer
的任務執行完成,如果設定成daemon
,那麼當main
執行緒結束的時候,程式只剩下timer
這個daemon
執行緒,於是程式不會等timer
執行緒執行task
就終止了。
有些時候,程式的終止與否並不只與timer
執行緒有關。舉個例子,如果我們使用AWT
來beep
,那麼AWT
會自動建立一個非daemon
執行緒來保持程式的執行。下面的程式碼我們對Reminder
做了修改,加入了beeping
功能,於是我們需要加入System.exit
的呼叫來終止程式。
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 反覆執行一個任務
先看一個例子:
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)
}
}
}
...
}
執行,你會看到如下輸出:
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
這裡使用了三個引數的schedule
方法用來指定task
每隔一秒執行一次。
如下所列為所有Time
r類用來制定計劃反覆執行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)
當計劃反覆執行的任務時,如果注重任務執行的平滑度,那麼請使用schedule
方法,如果你在乎的是任務的執行頻度那麼使用 scheduleAtFixedRate
方法。 例如,這裡使用了schedule
方法,這就意味著所有beep
之間的時間間隔至少為1
秒,也就是說,如果有一個beap
因為某種原因遲到了(未按計劃執行),那麼餘下的所有beep
都要延時執行。如果想讓這個程式正好在3
秒以後終止,無論哪一個beep
因為什麼原因被延時,那麼需要使用scheduleAtFixedRate
方法,這樣當第一個beep
遲到時,那麼後面的beep
就會以最快的速度緊密執行(最大限度的壓縮間隔時間)
4 進一步分析schedule和scheduleAtFixedRate
2
個引數的schedule
在制定任務計劃時, 如果指定的計劃執行時間scheduledExecutionTime<=systemCurrentTime
,則task
會被立即執行。scheduledExecutionTime
不會因為某一個task
的過度執行而改變。
3
個引數的schedule
在制定反覆執行一個task
的計劃時,每一次執行這個task
的計劃執行時間隨著前一次的實際執行時間而變,也就是scheduledExecutionTime
(第n+1
次)=realExecutionTime
(第n
次)+periodTime
。
也就是說如果第n
次執行task
時,由於某種原因這次執行時間過長,執行完後的systemCurrentTime
>=scheduledExecutionTime
(第n+1
次),則此時不做時隔等待,立即執行第n+1
次task
,而接下來的第n+2
次task
的scheduledExecutionTime
(第n+2
次)就隨著變成了realExecutionTime
(第n+1
次)+periodTime
。說白了,這個方法更注重保持間隔時間的穩定。
3
個引數的scheduleAtFixedRate
在制定反覆執行一個task
的計劃時,每一次執行這個task
的計劃執行時間在最初就被定下來了,也就是 scheduledExecutionTime
(第n次)=firstExecuteTime+n*periodTime
;如果第n
次執行task
時,由 於某種原因這次執行時間過長,執行完後的systemCurrentTime>=scheduledExecutionTime
(第n+1
次),則 此時不做period
間隔等待,立即執行第n+1
次task
,而接下來的第n+2
次的task
的scheduledExecutionTime
(第n+2
次)依然還是firstExecuteTime+(n+2)*periodTime
這在第一次執行task
就定下來了。說白了,這個方法更注重保持執行頻率的穩定。
5 一些注意的問題
每一個Timer
僅對應唯一一個執行緒
Timer
不保證任務執行的十分精確
Timer
類的執行緒安全的