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()方法
    調用timercancle方法。可以從程序的任何地方調用此方法,甚至在一個timer taskrun方法里
  • 成為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線程有關。舉個例子,如果我們使用AWTbeep,那么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每隔一秒執行一次。
如下所列為所有Timer類用來制定計划反復執行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+1task,而接下來的第n+2taskscheduledExecutionTime(第n+2次)就隨着變成了realExecutionTime(第n+1次)+periodTime。說白了,這個方法更注重保持間隔時間的穩定。

3個參數的scheduleAtFixedRate在制定反復執行一個task的計划時,每一次執行這個task的計划執行時間在最初就被定下來了,也就是 scheduledExecutionTime(第n次)=firstExecuteTime+n*periodTime;如果第n次執行task時,由 於某種原因這次執行時間過長,執行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),則 此時不做period間隔等待,立即執行第n+1task,而接下來的第n+2次的taskscheduledExecutionTime(第n+2次)依然還是firstExecuteTime+(n+2)*periodTime這在第一次執行task就定下來了。說白了,這個方法更注重保持執行頻率的穩定。

5 一些注意的問題

每一個Timer僅對應唯一一個線程
Timer不保證任務執行的十分精確
Timer類的線程安全的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM