1. 程式人生 > >Java:如何正確使用Timer【java定時器的使用(Timer) 】

Java:如何正確使用Timer【java定時器的使用(Timer) 】

在需要按時間計劃執行簡單任務的情況下,Timer是最常被使用到的工具類。使用Timer來排程TimerTask的實現者來執行任務,有兩種方式,一種是使任務在指定時間被執行一次,另一種是從某一指定時間開始週期性地執行任務。
 
下面是一個簡單的Timer例子,它每隔10秒鐘執行一次特定操作doWork。
Timer timer = new Timer();
                        TimerTask  task = new TimerTask (){
                        public void run() {
                             doWork();
                           }
};
              timer.schedule (task, 10000L, 10000L);
可以看到,具體的任務由TimerTask的子類實現,Timer負責管理、執行TimerTask。
 
Timer 的使用
在不同的場景下,需要使用不同的Timer介面。如上所說,主要區分兩種情況
1) 在指定時間執行任務,只執行一次
-          public void schedule(TimerTask task, long delay)
-          public void schedule(TimerTask task, Date time)
 
2)從指定時間開始,週期性地重複執行,直到任務被cancel掉。其中又分兩種型別:
2.1) 一種是按上一次任務執行的時間為依據,計算本次執行時間,可以稱為相對時間法。比如,如果第一次任務是1分10秒執行的,週期為5秒,因系統繁忙(比如垃 圾回收、虛擬記憶體切換),1分15秒沒有得到機會執行,直到1分16秒才有機會執行第二次任務,那麼第3次的執行時間將是1分21秒,偏移了1秒。
-          public void schedule(TimerTask task, long delay, long period)
-          public void schedule(TimerTask task, Date firstTime, long period)
 
2.2) 另一種是絕對時間法,以使用者設計的起始時間為基準,第n次執行時間為“起始時間+n*週期時間”。比如,在上面的情況下,雖然因為系統繁忙,第二執行時間被推後1秒,但第3次的時間點仍然應該是1分20秒。
-          public void scheduleAtFixedRate(TimerTask task, long delay, long period)
-          public void scheduleAtFixedRate(TimerTask task, Date firstTime,                                    long period)
 
相 對時間法,關注於滿足短時間內的執行間隔,絕對時間法,則更關注在一個長時間範圍內,任務被執行的次數。如果我們要編寫一個程式,用timer控制文件編 輯器中提示游標的閃爍,用哪種更合適? 當然是相對時間法。如果改用絕對時間法,當從系統繁忙狀態恢復後,游標會快速連續閃爍多次,以彌補回在系統繁忙期間沒有被執行的任務,這樣的情況會使用者來 說比較難以接受。又如,每10分鐘檢查一次新郵件的到來,也適合於使用相對時間法。
Timer timer = new Timer();
                        TimerTask  task = new TimerTask (){
                         public void run() {
                                displayCursor();
                           }
};
              timer.schedule (task, 1000L, 1000L); //每秒閃爍一次游標
 
作為對比,我們來考慮一種絕對時間法的應用場景——倒數任務,比如,要求在10秒內做倒數計時,每秒做一次doworkPerSecond操作,10秒結束時做一次doworkEnd操作,然後結束任務。
Timer timer = new Timer();
                        TimerTask  task = new TimerTask (){
                              private int count=10;
                              public void run() {
                                   if(count>0){
                                      doWorkPerSecond();
                                     count--;
                            }else{
                               doWorkEnd();
                               cancel();
                             }
                           }
};
                        timer. scheduleAtFixedRate (task, 1000L, 1000L);
Timer及相關類的內部實現
Timer的內部會啟動一個執行緒TimerThread。即使有多個任務被加入這個Timer,它始終只有一個執行緒來管理這些任務。
-          TimerThread是Thread的子類。加入Timer的所有任務都會被最終放入TimerThread所管理的TaskQueue中。 TimerThread會不斷檢視TaskQueue中的任務,取出當前時刻應該被執行的任務執行之,並且會重新計算該任務的下一次執行時間,重新放入 TaskQueue。直到所有任務執行完畢(單次任務)或者被cancel(重複執行的任務),該執行緒才會結束。
-          TaskQueue,由陣列實現的二叉堆,堆的排序是以任務的下一次執行時間為依據的。二叉堆的使用使得TimerThread以簡潔高效的方式快速找到 當前時刻需要執行的TimerTask,因為,堆排序的特性是保證最小(或者最大)值位於堆疊頂端,在這裡,queue[1]始終是下次執行時間 (nextExecutionTime)最小的,即應該最先被執行的任務
 
比如,同一個timer管理兩個任務task1和task2
timer.schedule (task1, 4000L, 10000L);
timer. scheduleAtFixedRate (task2, 2000L, 15000L);
則,TaskQueue 中會有兩個任務:task1和task2。task2會排在頭部queue[1],當task2執行時間到,task2被執行,同時修改其 nextExecutionTime =當前的nextExecutionTime +15000L(絕對時間法)並重新在二叉堆中排序。排序後,task1被放到頭部。當task1執行時間到,task1被執行,並修改其 nextExecutionTime =當前時間+10000L,然後重新在二叉堆中對其排序………
 
一個例子
當收到客戶端請求時,服務端生成一個Response物件。服務端希望客戶端訪問該物件的間隔時間不能超過20秒,否則,服務端認為客戶端已經異常關閉或者網路異常,此時銷燬掉該物件並列印錯誤日誌。每次訪問都會重新開始計時。
 
class Response{
private TimerTask  timeout;
            public void init(){
         ………
Timer timer = new Timer();
timeout = new TimeOutTask();
                        timer.schedule (timeout, 20000L);
}
 
public void invoke(){
    timeout.cancel();//取消當前的timeout任務
; ….
   timeout = new TimeOutTask();
                           timer.schedule (timeout, 20000L);//重新開始計時
}
 
void destroy(){
   ……..
}
 
class TimeOutTask extends TimerTask{
           public void run() {
TraceTool.error(“Time out, destroy the Response object.”);
destroy();
                           }
}
}
 
因為Timer不支援對任務重置計時,所以此處採取了先cancel當前的任務再重新加入新任務來達到重置計時的目的。注意,對一個已經cancel的任務,不能通過schedule重新加入Timer中執行。TimerTask的狀態機如下:
 

一個新生成的TimerTask其狀態為VIRGIN,Timer只接受狀態為VIRGIN的任務,否則會有IllegalStateException異常丟擲。
呼叫任務的cancel方法,該任務就轉入CANCELLED狀態,並很快從TaskQueue中刪除。對單次執行的任務,一旦執行結束,該任務也會從中刪除。這意味著TimerTask將不再被timer所執行了。

Java定時器的使用(Timer

1、在應用開發中,經常需要一些週期性的操作,比如每5分鐘執行某一操作等。

對於這樣的操作最方便、高效的實現方式就是使用java.util.Timer工具類。

private java.util.Timer timer;

timer = new Timer(true);

timer.schedule(

new java.util.TimerTask() { public void run()

{ //server.checkNewMail(); 要操作的方法} }, 0, 5*60*1000);

第一個引數是要操作的方法,第二個引數是要設定延遲的時間,第三個參

數是週期的設定,每隔多長時間執行該操作。

使用這幾行程式碼之後,Timer本身會每隔5分鐘呼叫一遍

server.checkNewMail()方法,不需要自己啟動執行緒。Timer本身也是多執行緒同

步的,多個執行緒可以共用一個Timer,不需要外部的同步程式碼。

2

(1)Timer.schedule(TimerTask task,Date time)安排在制定的時間執行指定的

任務。

(2)Timer.schedule(TimerTask task,Date firstTime ,long period)安排指定

的任務在指定的時間開始進行重複的固定延遲執行.

(3)Timer.schedule(TimerTask task,long delay)安排在指定延遲後執行指定的

任務.

(4)Timer.schedule(TimerTask task,long delay,long period)安排指定的任務

從指定的延遲後開始進行重複的固定延遲執行.

(5)Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period)

安排指定的任務在指定的時間開始進行重複的固定速率執行.

(6)Timer.scheduleAtFixedRate(TimerTask task,long delay,long period)

排指定的任務在指定的延遲後開始進行重複的固定速率執行.

Java Timer API進行時間排程開發的相關注意點

java.util這個包中可以找到TimerTimerTask這兩個類。Timer直接從Object

繼承,它相當於一個計時器,能夠用它來指定某個時間來執行一項任務,或者

每隔一定時間間隔反覆執行同一個任務。建立一個Timer後,就會生成一個執行緒

在背後執行,來控制任務的執行。而TimerTask就是用來實現某項任務的類,

它實現了Runnable介面,因此相當於一個執行緒。

如何實現自己的任務排程?

1、繼承TimerTask,注意TimerTask是實現Runnable介面的,因此只要過載run()

方法即可。

2、建立Timer物件,呼叫schedule()方法。

相關注意點分析:

1、任務排程要優先考慮實時保證

由於Java的天性,並且在開發JDK的過程中要考慮到不同平臺,而不同平臺的

執行緒排程機制是不同的,因此各種平臺下JVM的執行緒排程機制也是不一致的。

從而Timer不能保證任務在所指定的時間內執行。另外由於TimerTask是實現

Runnable介面的,在TimerTask被放進執行緒佇列睡眠一段時間(wait)之後,

當到了指定的該喚起該TimerTask時,由於執行的確切時機取決於JVM的排程策

略和當前還有多少執行緒在等待CPU處理。因此就不能保證任務在所指定的時間

內執行。通常在如下兩種情況下導致任務延遲執行:

1)、有大量執行緒在等待執行

2)、GC機制的影響導致延遲

這也是為什麼在Timer API中存在兩組排程方法的原因。即:

1)、schedule()

用固定延遲排程。使用本方法時,在任務執行中的每一個延遲會傳播到後續的任

務的執行。

2)、scheduleAsFixedRate()

用固定比率排程。使用本方法時,所有後續執行根據初始執行的時間進行排程,

從而希望減小延遲。

具體使用哪一個方法取決於哪些引數對你的程式或系統更重要。

2、每個Timer物件要在後臺啟動一個執行緒。這種性質在一些託管的環境下不推

薦使用,比如在應用伺服器中。因為這些執行緒不在容器的控制範圍之內了。

具體Java API中的Timer類和TimerTask類的描述如下:

java.util

Timer

java.lang.Object

java.util.Timer

public class Timer

extends Object

一種執行緒設施,用於安排以後在後臺執行緒中執行的任務。可安排任務執行一次,

或者定期重複執行。

與每個Timer物件相對應的是單個後臺執行緒,用於順序地執行所有計時器任務。

計時器任務應該迅速完成。如果完成某個計時器任務的時間太長,那麼它會“獨

佔”計時器的任務執行執行緒。因此,這就可能延遲後續任務的執行,而這些任務

就可能“堆在一起”,並且在上述令人討厭的任務最終完成時才能夠被快速連續

地執行。

Timer物件最後的引用完成後,並且所有未處理的任務都已執行完成後,計

時器的任務執行執行緒會正常終止(並且成為垃圾回收的物件)。但是這可能要很

長時間後才發生。預設情況下,任務執行執行緒並不作為守護執行緒來執行,所以

它能夠阻止應用程式終止。如果呼叫方想要快速終止計時器的任務執行執行緒,那

麼呼叫方應該呼叫計時器的cancel方法。

如果意外終止了計時器的任務執行執行緒,例如呼叫了它的stop方法,那麼所有

以後對該計時器安排任務的嘗試都將導致IllegalStateException,就好像呼叫

了計時器的cancel方法一樣。

此類是執行緒安全的:多個執行緒可以共享單個Timer物件而無需進行外部同步。

此類提供實時保證:它使用Object.wait(long)方法來安排任務。

實現注意事項:此類可擴充套件到大量同時安排的任務(存在數千個都沒有問題)。

在內部,它使用二進位制堆來表示其任務佇列,所以安排任務的開銷是O(log n)

其中n 是同時安排的任務數。

實現注意事項:所有構造方法都啟動計時器執行緒。

從以下版本開始:

1.3

另請參見:

TimerTask,Object.wait(long)

構造方法摘要

Timer()

建立一個新計時器。

Timer(boolean isDaemon)

建立一個新計時器,可以指定其相關的執行緒作為守護程式執行。

Timer(Stringname)

建立一個新計時器,其相關的執行緒具有指定的名稱。

Timer(Stringname, boolean isDaemon)

建立一個新計時器,其相關的執行緒具有指定的名稱,並且可以指定作為守護程式運

行。

方法摘要

void cancel()

終止此計時器,丟棄所有當前已安排的任務。

int purge()

從此計時器的任務佇列中移除所有已取消的任務。

void schedule(TimerTasktask,Date time)

安排在指定的時間執行指定的任務。

void schedule(TimerTasktask,Date firstTime, long period)

安排指定的任務在指定的時間開始進行重複的固定延遲執行

void schedule(TimerTasktask, long delay)

安排在指定延遲後執行指定的任務。

void schedule(TimerTasktask, long delay, long period)

安排指定的任務從指定的延遲後開始進行重複的固定延遲執行

void scheduleAtFixedRate(TimerTasktask,Date firstTime, long period)

安排指定的任務在指定的時間開始進行重複的固定速率執行

void scheduleAtFixedRate(TimerTasktask, long delay, long period)

安排指定的任務在指定的延遲後開始進行重複的固定速率執行

從類java.lang.Object繼承的方法

clone, equals, finalize,getClass,hashCode, notify,notifyAll,toString, wait,wait,wait

構造方法詳細資訊

Timer

public Timer()

建立一個新計時器。相關的執行緒作為守護程式執行。

另請參見:

Thread,cancel()

Timer

public Timer(boolean isDaemon)

建立一個新計時器,可以指定其相關的執行緒作為守護程式執行。如果計時器將用於

安排重複的維護活動,則呼叫守護執行緒,在應用程式執行期間必須呼叫守護執行緒,

但是該操作不應延長程式的生命週期。

引數:

isDaemon -如果應該將相關的執行緒作為守護程式執行,則為true

另請參見:

Thread,cancel()

Timer

public Timer(Stringname)

建立一個新計時器,其相關的執行緒具有指定的名稱。相關的執行緒作為守護程式運

行。

引數:

name -相關執行緒的名稱。

丟擲:

NullPointerException -如果namenull

從以下版本開始:

1.5

另請參見:

Thread.getName(),Thread.isDaemon()

Timer

public Timer(Stringname,

boolean isDaemon)

建立一個新計時器,其相關的執行緒具有指定的名稱,並且可以指定作為守護程式運

行。

引數:

name -相關執行緒的名稱。

isDaemon -如果應該將相關的執行緒作為守護程式執行,則為true

丟擲:

NullPointerException -如果namenull

從以下版本開始:

1.5

另請參見:

Thread.getName(),Thread.isDaemon()

方法詳細資訊

schedule

public void schedule(TimerTasktask,

long delay)

安排在指定延遲後執行指定的任務。

引數:

task -所要安排的任務。

delay -執行任務前的延遲時間,單位是毫秒。

丟擲:

IllegalArgumentException -如果delay是負數, 或者delay +

System.currentTimeMillis() 是負數。

IllegalStateException -如果已經安排或取消了任務,或者已經取消計時器。

schedule

public void schedule(TimerTasktask,

Date time)

安排在指定的時間執行指定的任務。如果此時間已過去,則安排立即執行該任務。

引數:

task -所要安排的任務。

time -執行任務的時間。

丟擲:

IllegalArgumentException -如果time.getTime()是負數。

IllegalStateException -如果已經安排或取消了任務,已經取消了計時器,或者計時

器執行緒已終止。

schedule

public void schedule(TimerTasktask,

long delay,

long period)

安排指定的任務從指定的延遲後開始進行重複的固定延遲執行。以近似固定的時間

間隔(由指定的週期分隔)進行後續執行。

在固定延遲執行中,根據前一次執行的實際執行時間來安排每次執行。如

果由於任何原因(如垃圾回收或其他後臺活動)而延遲了某次執行,則後

續執行也將被延遲。從長期來看,執行的頻率一般要稍慢於指定週期的倒

數(假定Object.wait(long)所依靠的系統時鐘是準確的)。

固定延遲執行適用於那些需要“平穩”執行的重複活動。換句話說,它適

用於在短期執行中保持頻率準確要比在長期執行中更為重要的活動。這包

括大多數動畫任務,如以固定時間間隔閃爍的游標。這還包括為響應人類

活動所執行的固定活動,如在按住鍵時自動重複輸入字元。

引數:

task -所要安排的任務。

delay -執行任務前的延遲時間,單位是毫秒。

period -執行各後續任務之間的時間間隔,單位是毫秒。

丟擲:

IllegalArgumentException -如果delay是負數, 或者delay +

System.currentTimeMillis() 是負數。

IllegalStateException -如果已經安排或取消了任務,已經取消了計時器,或者計時

器執行緒已終止。

schedule

public void schedule(TimerTasktask,

Date firstTime,

long period)

安排指定的任務在指定的時間開始進行重複的固定延遲執行。以近似固定的時間間

隔(由指定的週期分隔)進行後續執行。

在固定延遲執行中,根據前一次執行的實際執行時間來安排每次執行。如

果由於任何原因(如垃圾回收或其他後臺活動)而延遲了某次執行,則後

續執行也將被延遲。在長期執行中,執行的頻率一般要稍慢於指定週期的

倒數(假定Object.wait(long)所依靠的系統時鐘是準確的)。

固定延遲執行適用於那些需要“平穩”執行的重複執行活動。換句話說,

它適用於在短期執行中保持頻率準確要比在長期執行中更為重要的活動。

這包括大多數動畫任務,如以固定時間間隔閃爍的游標。這還包括為響應

人類活動所執行的固定活動,如在按住鍵時自動重複輸入字元。

引數:

task -所要安排的任務。

firstTime -首次執行任務的時間。

period -執行各後續任務之間的時間間隔,單位是毫秒。

丟擲:

IllegalArgumentException -如果time.getTime()是負數。

IllegalStateException -如果已經安排或取消了任務,已經取消了計時器,或者計時

器執行緒已終止。

scheduleAtFixedRate

public void scheduleAtFixedRate(TimerTasktask,

long delay,

long period)

安排指定的任務在指定的延遲後開始進行重複的固定速率執行。以近似固定的時間

間隔(由指定的週期分隔)進行後續執行。

在固定速率執行中,根據已安排的初始執行時間來安排每次執行。如果由

於任何原因(如垃圾回收或其他背景活動)而延遲了某次執行,則將快速

連續地出現兩次或更多的執行,從而使後續執行能夠“追趕上來”。從長

遠來看,執行的頻率將正好是指定週期的倒數(假定Object.wait(long)

所依靠的系統時鐘是準確的)。

固定速率執行適用於那些對絕對時間敏感的重複執行活動,如每小時準

點打鐘報時,或者在每天的特定時間執行已安排的維護活動。它還適用於

那些完成固定次數執行的總計時間很重要的重複活動,如倒計時的計時

器,每秒鐘滴答一次,共10秒鐘。最後,固定速率執行適用於安排多個

重複執行的計時器任務,這些任務相互之間必須保持同步。

引數:

task -所要安排的任務。

delay -執行任務前的延遲時間,單位是毫秒。

period -執行各後續任務之間的時間間隔,單位是毫秒。

丟擲:

IllegalArgumentException -如果delay是負數, 或者delay +

System.currentTimeMillis() 是負數。

IllegalStateException -如果已經安排或取消了任務,已經取消了計時器,或者計時

器執行緒已終止。

scheduleAtFixedRate

public void scheduleAtFixedRate(TimerTasktask,

Date firstTime,

long period)

安排指定的任務在指定的時間開始進行重複的固定速率執行。以近似固定的時間間

隔(由指定的週期分隔)進行後續執行。

在固定速率執行中,相對於已安排的初始執行時間來安排每次執行。如果

由於任何原因(如垃圾回收或其他背景活動)而延遲了某次執行,則將快

速連續地出現兩次或更多次執行,從而使後續執行能夠趕上來。從長遠來

看,執行的頻率將正好是指定週期的倒數(假定Object.wait(long)

依靠的系統時鐘是準確的)。

固定速率執行適用於那些對絕對時間敏感的重複執行活動,如每小時準

點打鐘報時,或者在每天的特定時間執行已安排的維護活動。它還適用於

那些完成固定次數執行的總計時間很重要的重複活動,如倒計時的計時

器,每秒鐘滴答一次,共10秒鐘。最後,固定速率執行適用於安排多次

重複執行的計時器任務,這些任務相互之間必須保持同步。

引數:

task -所要安排的任務。

firstTime -首次執行任務的時間。

period -執行各後續任務之間的時間間隔,單位是毫秒。

丟擲:

IllegalArgumentException -如果time.getTime()是負數。

IllegalStateException -如果已經安排或取消了任務,已經取消了計時器,或者計時

器執行緒已終止。

cancel

public void cancel()

終止此計時器,丟棄所有當前已安排的任務。這不會干擾當前正在執行的任務(如

果存在)。一旦終止了計時器,那麼它的執行執行緒也會終止,並且無法根據它安排更

多的任務。

注意,在此計時器呼叫的計時器任務的run方法內呼叫此方法,就可以

絕對確保正在執行的任務是此計時器所執行的最後一個任務。

可以重複呼叫此方法;但是第二次和後續呼叫無效。

purge

public int purge()

從此計時器的任務佇列中移除所有已取消的任務。呼叫此方法對計時器的行為沒有

影響,但是將無法引用佇列中已取消的任務。如果沒有對這些任務的外部引用,則

它們就成為垃圾回收的合格物件。

多數程式無需呼叫此方法。它設計用於一些罕見的應用程式,這些程式可

取消大量的任務。呼叫此方法要以時間來換取空間:此方法的執行時可能

n + c log n呈正比,其中n是佇列中的任務數,而c 是取消的任

務數。

注意,從此計時器上所安排的任務中呼叫此方法是允許的。

返回:

從佇列中移除的任務數。

從以下版本開始:

1.5

下面是TimerTask類的介紹

java.util

TimerTask

java.lang.Object

java.util.TimerTask

所有已實現的介面:

Runnable

public abstract class TimerTask

extends Object

implements Runnable

Timer安排為一次執行或重複執行的任務。

從以下版本開始:

1.3

另請參見:

Timer

構造方法摘要

protected TimerTask()

建立一個新的計時器任務。

方法摘要

boolean cancel()

取消此計時器任務。

abstract void run()

此計時器任務要執行的操作。

long scheduledExecutionTime()

返回此任務最近實際執行的安排執行時間。

從類java.lang.Object繼承的方法

clone, equals, finalize,getClass,hashCode, notify,notifyAll,toString, wait,wait,wait

構造方法詳細資訊

TimerTask

protected TimerTask()

建立一個新的計時器任務。

方法詳細資訊

run

public abstract void run()

此計時器任務要執行的操作。

指定者:

介面Runnable中的run

另請參見:

Thread.run()

cancel

public boolean cancel()

取消此計時器任務。如果任務安排為一次執行且還未執行,或者尚未安排,則永遠

不會執行。如果任務安排為重複執行,則永遠不會再執行。(如果發生此呼叫時任務

正在執行,則任務將執行完,但永遠不會再執行。)

注意,從重複的計時器任務的run方法中呼叫此方法絕對保證計時器任

務永遠不會再執行。

此方法可以反覆呼叫;第二次和以後的呼叫無效。

返回:

如果此任務安排為一次執行且尚未執行,或者此任務安排為重複執行,則返回true

如果此任務安排為一次執行且已經執行,或者此任務尚未安排,或者此任務已經取

消,則返回false。(一般來說,如果此方法阻止發生一個或多個安排執行,則返回

true。)

scheduledExecutionTime

public long scheduledExecutionTime()

返回此任務最近實際執行的安排執行時間。(如果在任務執行過程中呼叫此方法,

則返回值為此任務執行的安排執行時間。)

通常從一個任務的run方法中呼叫此方法,以確定當前任務執行是否能

充分及時地保證完成安排活動:

public void run() {

if (System.currentTimeMillis() - scheduledExecutionTime()

>=

MAX_TARDINESS)

return; // Too late; skip this execution.

// Perform the task

}

通常,此方法固定延遲執行的重複任務一起使用,因為其安排執行時間允許

隨時間浮動,所以毫無意義。

返回:

最近發生此任務執行安排的時間,採用Date.getTime()返回的格式。如果任務已開

始其首次執行,則返回值不確定。

另請參見:

Da