先看個小測試程序:
package?test;
import?java.util.Timer;
import?java.util.TimerTask;
public?class?TestTimer?extends?TimerTask{
????int?number;
????/**
?????*?@param?args
?????*/
????public?static?void?main(String[]?args)?{
????????Timer?timer=new?Timer();
????????timer.schedule(new?TestTimer(), 1000, 1000);
????????
????}
????public?void?run()?{
???????System.out.println("number="+(++number));
????????
????}
}
正常情況該程序每秒打印當前的 number值。
現在我們來改變這個正常情況:
1)保持程序運行
2)通過系統時間欄把系統時間往前調一天。
過一會兒,你會發現該程序停止輸出了?
對,要看到下一個輸出,你得等一天了,在這種情況下,你即使現在重新調整到正確系統時間,你仍然得等到下一天才能看到輸出。
為什么呢?
下面是JDK中Timer調度的核心代碼(在原有注釋的基礎上加了一些):
?/**
?????*?The?main?timer?loop.??(See?class?comment.)
?????*/
????private?void?mainLoop()?{
????????while?(true)?{
????????????try?{
????????????????//to?be?scheduled?task
????????????????TimerTask?task;
????????????????//fired?one?task?
????????????????boolean?taskFired;
????????????????synchronized(queue)?{
????????????????????//?Wait?for?queue?to?become?non-empty
????????????????????while?(queue.isEmpty()?&&?newTasksMayBeScheduled)
????????????????????????queue.wait();
????????????????????if?(queue.isEmpty())
????????????????????????break;?//?Queue?is?empty?and?will?forever?remain;?die
????????????????????//?Queue?nonempty;?look?at?first?evt?and?do?the?right?thing
????????????????????long?currentTime,?executionTime;
????????????????????task?=?queue.getMin();
????????????????????synchronized(task.lock)?{
????????????????????????if?(task.state?==?TimerTask.CANCELLED)?{
????????????????????????????//find?the?first?task?from?the?queue.
????????????????????????????queue.removeMin();
????????????????????????????continue;??//?No?action?required,?poll?queue?again
????????????????????????}
????????????????????????//here?,use?system?current?time?to?determin?whether?or?not?fire?a?task
????????????????????????currentTime?=?System.currentTimeMillis();
????????????????????????executionTime?=?task.nextExecutionTime;
????????????????????????//So?when?we?change?system?time?to?long?ago,this?expression?will?be?evaluate?to?false
????????????????????????if?(taskFired?=?(executionTime<=currentTime))?{
????????????????????????????if?(task.period?==?0)?{?//?Non-repeating,?remove
????????????????????????????????queue.removeMin();
????????????????????????????????task.state?=?TimerTask.EXECUTED;
????????????????????????????}?else?{?//?Repeating?task,?reschedule
????????????????????????????????queue.rescheduleMin(
??????????????????????????????????task.period<0???currentTime???-?task.period
????????????????????????????????????????????????:?executionTime?+?task.period);
????????????????????????????}
????????????????????????}
????????????????????}
????????????????????if?(!taskFired)?//?Task?hasn't?yet?fired;?wait
????????????????????????queue.wait(executionTime?-?currentTime);
????????????????}
????????????????if?(taskFired)??//?Task?fired;?run?it,?holding?no?locks
????????????????????task.run();
????????????}?catch(InterruptedException?e)?{
????????????}
????????}
????}
那么,我們如何能讓Timer調度不依賴這種系統時間呢?
Windows API中GetTickCount()返回系統自啟動以來的毫秒數。我們可以用這個來替代System.currentTimeMillis().這樣即使改變系統時間
Timer的調度也不會所影響。
改完后再來測試一把
public?class?TestTimer2?extends?com.yovn.labs.util.TimerTask{
????int?number;
????/**
?????*?@param?args
?????*/
????public?static?void?main(String[]?args)?{
????????com.yovn.labs.util.Timer?timer=new?com.yovn.labs.util.Timer();
????????timer.schedule(new?TestTimer2(),?1000,?1000);
????????
????}
????public?void?run()?{
???????System.out.println("number="+(++number));
????????
????}
}
這時的輸出沒有再受系統時間的改變了。
本文代碼下載:
source code