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
類的線程安全的