Timer的schedule和scheduleAtFixedRate方法辨析

 首先我們來看看在API中是如何描述java.util.Timer類的。(以下摘自Java 1.5  API )

       與每個 Timer 對象相對應的是單個後臺線程,用於順序地執行所有計時器任務。計時器任務應該迅速完成。如果完成某個計時器任務的時間太長,那麼它會“獨佔”計時器的任務執行線程。因此,這就可能延遲後續任務的執行,而這些任務就可能“堆在一起”,並且在上述令人討厭的任務最終完成時才能夠被快速連續地執行。對 Timer 對象最後的引用完成後,並且所有未處理的任務都已執行完成後,計時器的任務執行線程會正常終止(並且成爲垃圾回收的對象)。但是這可能要很長時間後才發生。默認情況下,任務執行線程並不作爲守護線程來運行,所以它能夠阻止應用程序終止。如果調用方想要快速終止計時器的任務執行線程,那麼調用方應該調用計時器的 cancel 方法。如果意外終止了計時器的任務執行線程,例如調用了它的 stop 方法,那麼所有以後對該計時器安排任務的嘗試都將導致 IllegalStateException,就好像調用了計時器的 cancel 方法一樣。此類是線程安全的:多個線程可以共享單個 Timer 對象而無需進行外部同步。

    Timer中的void schedule(TimerTask task, Date firstTime,long period) ,

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

   Timer中的void scheduleAtFixedRate(TimerTask task, Date firstTime, long period),

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

   大家這兩個描述,可以看出什麼不同來嗎?至少我是沒有看出來哈!現在我就用一個典型的案例來辨析一下這兩者的不同之處!

1.void  scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 方法測試:

public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  
    Date startDate = dateFormatter.parse("2010/11/28 01:06:00"); 
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
  public void run() {
  System.out.println("進入任務的時間: "+new Date());
  try {
  Thread.sleep(4000);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  ///注意這裏是安排任務應該執行的時間
      System.out.println("計劃開始執行execute task: "+ new  Date(this.scheduledExecutionTime()));
      //注意這裏是任務實際的執行時間
  System.out.println("任務結束時間: "+new Date());
  }
},startDate, 2* 1000);
}
}

說明:scheduledExecutionTime()方法返回此任務最近實際 執行的安排 執行時間。

          這是在任務實際執行時間>程序設定的間隔時間   的情況下!

執行結果:

進入任務的時間: Thu Feb 13 15:39:26 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:00 CST 2010
任務結束時間: Thu Feb 13 15:39:30 CST 2014
進入任務的時間: Thu Feb 13 15:39:30 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:02 CST 2010
任務結束時間: Thu Feb 13 15:39:34 CST 2014
進入任務的時間: Thu Feb 13 15:39:34 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:04 CST 2010
任務結束時間: Thu Feb 13 15:39:38 CST 2014
進入任務的時間: Thu Feb 13 15:39:38 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:06 CST 2010
任務結束時間: Thu Feb 13 15:39:42 CST 2014
進入任務的時間: Thu Feb 13 15:39:42 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:08 CST 2010
任務結束時間: Thu Feb 13 15:39:46 CST 2014
進入任務的時間: Thu Feb 13 15:39:46 CST 2014

修改程序參數後如下:

public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  
    Date startDate = dateFormatter.parse("2010/11/28 01:06:00"); 
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
  public void run() {
  System.out.println("進入任務的時間: "+new Date());
  try {
  Thread.sleep(2000);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  ///注意這裏是安排任務應該執行的時間
      System.out.println("計劃開始執行execute task: "+ new  Date(this.scheduledExecutionTime()));
      //注意這裏是任務實際的執行時間
  System.out.println("任務結束時間: "+new Date());
  }
},startDate, 4* 1000);
}
}

注意:這是在任務實際執行時間<程序設定的間隔時間   的情況下!

執行結果:

進入任務的時間: Thu Feb 13 15:52:04 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:00 CST 2010
任務結束時間: Thu Feb 13 15:52:06 CST 2014
進入任務的時間: Thu Feb 13 15:52:06 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:04 CST 2010
任務結束時間: Thu Feb 13 15:52:08 CST 2014
進入任務的時間: Thu Feb 13 15:52:08 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:08 CST 2010
任務結束時間: Thu Feb 13 15:52:10 CST 2014
進入任務的時間: Thu Feb 13 15:52:10 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:12 CST 2010
任務結束時間: Thu Feb 13 15:52:12 CST 2014
進入任務的時間: Thu Feb 13 15:52:12 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:16 CST 2010
任務結束時間: Thu Feb 13 15:52:14 CST 2014
進入任務的時間: Thu Feb 13 15:52:14 CST 2014
計劃開始執行execute task: Sun Nov 28 01:06:20 CST 2010
任務結束時間: Thu Feb 13 15:52:16 CST 2014
進入任務的時間: Thu Feb 13 15:52:16 CST 2014

由此可以肯定的是:

(1)void  scheduleAtFixedRate(TimerTask task, Date firstTime, long period)方法的任務的計劃執行時間是從firstTime開始計算的。第一次:firstTime,第二次:firstTime+period.以此類推。

(2)void  scheduleAtFixedRate(TimerTask task, Date firstTime, long period)方法的下一個任務的實際開始執行時間=上一任務的實際開始執行時間+(實際執行時間與時間間隔中的最大值)。

2.void schedule(TimerTask task, Date firstTime,long period) 測試:

public class Test2 {
public static void main(String[] args) throws ParseException {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  
    Date startDate = dateFormatter.parse("2010/11/28 01:06:00"); 
Timer timer = new Timer();
timer.schedule(new TimerTask(){
  public void run() {
  try {
  System.out.println("進入任務的時間: "+new Date());
  //任務執行時間2秒
  Thread.sleep(2000);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  //注意這裏是安排任務應該執行的時間
      System.out.println("計劃開始執行execute task: "+ new  Date(this.scheduledExecutionTime()));
      //注意這裏是任務實際的執行時間
  System.out.println("任務結束時間: "+new Date());
  }
},startDate,4* 1000);
}
}

執行結果:

進入任務的時間: Thu Feb 13 15:57:38 CST 2014
計劃開始執行execute task: Thu Feb 13 15:57:38 CST 2014
任務結束時間: Thu Feb 13 15:57:40 CST 2014
進入任務的時間: Thu Feb 13 15:57:42 CST 2014
計劃開始執行execute task: Thu Feb 13 15:57:42 CST 2014
任務結束時間: Thu Feb 13 15:57:44 CST 2014
進入任務的時間: Thu Feb 13 15:57:46 CST 2014
計劃開始執行execute task: Thu Feb 13 15:57:46 CST 2014
任務結束時間: Thu Feb 13 15:57:48 CST 2014
進入任務的時間: Thu Feb 13 15:57:50 CST 2014
計劃開始執行execute task: Thu Feb 13 15:57:50 CST 2014
任務結束時間: Thu Feb 13 15:57:52 CST 2014

注意年份沒有變化!

這時候  任務執行時間<間隔時間  (2000>4000)!

上一個任務結束後等待2秒再進入下一個任務。

現在修改一下參數:

public class Test2 {
public static void main(String[] args) throws ParseException {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  
    Date startDate = dateFormatter.parse("2010/11/28 01:06:00"); 
Timer timer = new Timer();
timer.schedule(new TimerTask(){
  public void run() {
  try {
  System.out.println("進入任務的時間: "+new Date());
  Thread.sleep(4000);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  //注意這裏是安排任務應該執行的時間
      System.out.println("計劃開始執行execute task: "+ new  Date(this.scheduledExecutionTime()));
      //注意這裏是任務實際的執行時間
  System.out.println("任務結束時間: "+new Date());
  }
},startDate,2* 1000);
}
}

執行結果:

進入任務的時間: Thu Feb 13 16:02:11 CST 2014
計劃開始執行execute task: Thu Feb 13 16:02:11 CST 2014
任務結束時間: Thu Feb 13 16:02:15 CST 2014
進入任務的時間: Thu Feb 13 16:02:15 CST 2014

計劃開始執行execute task: Thu Feb 13 16:02:15 CST 2014
任務結束時間: Thu Feb 13 16:02:19 CST 2014
進入任務的時間: Thu Feb 13 16:02:19 CST 2014

計劃開始執行execute task: Thu Feb 13 16:02:19 CST 2014
任務結束時間: Thu Feb 13 16:02:23 CST 2014
進入任務的時間: Thu Feb 13 16:02:23 CST 2014

計劃開始執行execute task: Thu Feb 13 16:02:23 CST 2014
任務結束時間: Thu Feb 13 16:02:27 CST 2014
進入任務的時間: Thu Feb 13 16:02:27 CST 2014

計劃開始執行execute task: Thu Feb 13 16:02:27 CST 2014
任務結束時間: Thu Feb 13 16:02:31 CST 2014
進入任務的時間: Thu Feb 13 16:02:31 CST 2014

注意:這時候  任務執行時間>間隔時間  (4000>2000)!

上一個任務結束後馬上進入下一個任務。

由此可以推出:void schedule(TimerTask task, Date firstTime,long period)方法的任務的計劃執行時間是從第一次實際執行任務開始計算的。

a. 當任務執行時間>間隔時間時,第一次計劃執行時間=第一次實際開始執行任務的時間,第二次計劃執行時間==上一次開始執行時間+任務執行時間。以此類推。

b.當任務執行時間<間隔時間時,第一次計劃執行時間=第一次實際開始執行任務的時間,第二次計劃執行時間=上一次開始執行時間+間隔時間。以此類推。

這同時也論證了API描述部分說的任務執行時間長時會產生累計延遲的效果。

結論:void schedule(TimerTask task, Date firstTime,long period) 方法與void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)方法的不同之處在於計劃執行時間的方式不一樣。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章