Java Timer:schedule和scheduleAtFixedRate有何不同
在前一篇文章 Java Timer:排程、定時、週期性執行工作任務 中,
我們展示了如何利用 Timer 非常簡單地執行排程工作。
然而在 Timer 中有兩個用來排程的 method:schedule 和 scheduleAtFixedRate,
兩者最明顯的差異就是字面上的不同,後者多了 AtFixedRate,
可是在上一篇我們用 schedule 也可以重複執行工作啊?
到底這兩者差在哪裡呢?使用時機又該如何決定呢?
為了解決這個問題,我直接查了 Timer 的 source code,
其中最關鍵的差異為:
只要預訂要執行的時間已經過了,那麼兩者都會直接把工作移除不執行。
但若在有設定 period 的情況下,若預訂要執行的時間已經過了,
基於上面所描述的行為,schedule 沒有上次執行完成的時間,會從現在開始算並執行,
而 scheduleAtFixedRate 則以預訂執行的時間開始算,且會一口氣將過去未做的補上!
接著讓我們來看例子,首先我們要看的是當執行所需時間比執行間隔長和短時,
兩者的運作會有何差異:
下面是工作 Task 的程式碼,每次進到工作時會 random 睡上 4~8秒才結束。
首先來看 schedule 的部份,由下面的資料我們可以發現,
當工作執行時間超過 5 秒時,下次的預訂執行時間會以工作結束的時間來計算,
如下面標紅色部份,工作執行了 7秒,則下次的預訂時間就晚 7秒,
而若工作時間少於 5 秒,下次執行的間隔仍維持 5 秒,如下面標藍色部份。
對 schedule 而言,所有預訂和實際執行時間都是相同的,沒有 catch up 的情況。
當工作執行時間超過 5 秒時,下次的預訂執行時間仍以工作開始的時間來計算,
所以所有後續工作的預訂時間都是間隔 5秒,然而排程的工作不會同時執行,
故雖然預訂時間間隔5秒,但實際執行時間會被 delay,如下面標紅色部份,
工作執行了 6秒,則下次的預訂時間是5秒後,但實際執行是6秒後。
這個預訂時間和實際執行時間的差距,將在後續的工作排程中產生影響,
在實際執行時間落後預訂時間時,scheduleAtFixedRate 會有 catch up 的機制,
在後續若執行時間較短,我們就可以發現執行的間距小於 5秒,如藍色所示。
而是接續著執行,所以不須考慮同步的問題。
這點在 Timer的schedule和scheduleAtFixedRate方法的区别解析 的解釋是錯的,
因為他印出的都是 scheduledExecutionTime,而非實際執行時間。
接下來我們再來看看若預訂執行的時間已經過了,兩個不同 method 有何差異,
這也會展示 catch up 的影響到底有多大:
這次我們不再需要不同的工作時間,所以工作的程式碼簡化如下:
schedule 會直接從現在開始做,且沒有 catch up 的情況。
而 scheduleAtFixedRate 則會發生 catch up,即他會想要趕上預訂的執行時間,
所以已過期的任務仍會被執行,這也是為何藍色部份會在一開始被執行連續被執行。
從以上所有的測試結果來看,兩者主要的差異有兩點:
http://kickjava.com/src/java/util/Timer.java.htm
若想知道更多有關 Java 時間相關的轉換、排程等應用,請見:
Java 時間日期處理範例大全:含時間單位格式轉換、期間計算、排程等
關鍵字:java, timer, schedule, scheduleAtFixedRate, 差異, 不同, 比較, 差別, example, 例子, 範例,
參考資料:
我們展示了如何利用 Timer 非常簡單地執行排程工作。
然而在 Timer 中有兩個用來排程的 method:schedule 和 scheduleAtFixedRate,
兩者最明顯的差異就是字面上的不同,後者多了 AtFixedRate,
可是在上一篇我們用 schedule 也可以重複執行工作啊?
到底這兩者差在哪裡呢?使用時機又該如何決定呢?
為了解決這個問題,我直接查了 Timer 的 source code,
其中最關鍵的差異為:
- schedule為「fixed-delay」,執行時間參照前次工作執行完成的時間:
若執行工作沒被 delay,則按照預訂的時間執行;但若執行工作時被 delay了,
随後工作的預訂執行時間會按照上一次執行「完成」的時間點來計算。 - scheduleAtFixedRate為「fixed-rate」,執行時間參照一開始的時間點;
和schedule一樣,若執行工作沒被 delay,則按照預訂的時間執行;
但如果執行工作時被delay了,
後續的預訂執行時間仍按照上一次執行「開始」的時間點計算,
且為了「catch up」預訂時間,會連續多次補足未完成的任務!
只要預訂要執行的時間已經過了,那麼兩者都會直接把工作移除不執行。
但若在有設定 period 的情況下,若預訂要執行的時間已經過了,
基於上面所描述的行為,schedule 沒有上次執行完成的時間,會從現在開始算並執行,
而 scheduleAtFixedRate 則以預訂執行的時間開始算,且會一口氣將過去未做的補上!
接著讓我們來看例子,首先我們要看的是當執行所需時間比執行間隔長和短時,
兩者的運作會有何差異:
下面是工作 Task 的程式碼,每次進到工作時會 random 睡上 4~8秒才結束。
1 package werdna1222coldcodes.blogspot.com.demo.timer; 2 3 import java.util.*; 4 5 public class DateTaskSleep4to8s extends TimerTask { 6 public void run() { 7 System.out.println("Task 預訂執行時間:" 8 + new Date(this.scheduledExecutionTime()) 9 + ", \n實際執行時間:" + new Date()); 10 try { 11 int sleepSeconds = (int) (4 + Math.random()*4); 12 System.out.println( 13 "Task going to sleep " + sleepSeconds + "s."); 14 Thread.sleep(sleepSeconds*1000); 15 } 16 catch(InterruptedException e) { 17 } 18 } 19 }下面則是測試程式的程式碼,預訂的時間間隔為 5秒:
1 package werdna1222coldcodes.blogspot.com.demo.timer; 2 3 import java.util.*; 4 5 public class TimerScheduleAndScheduleAtFixedRateDemo { 6 public static void main(String[] args) { 7 8 TimerScheduleAndScheduleAtFixedRateDemo timerDemo 9 = new TimerScheduleAndScheduleAtFixedRateDemo(); 10 timerDemo.testSchedule(); 11 timerDemo.testScheduleAtFixedRate(); 12 } 13 14 void testSchedule(){ 15 16 Timer timer = new Timer(); 17 System.out.println("In testSchedule:" + new Date()); 18 System.out.println("Delay:5秒, Period:5秒"); 19 20 // schedule(TimerTask task, long delay, long period) 21 timer.schedule(new DateTaskSleep4to8s(), new Date(), 5000); 22 23 try { 24 Thread.sleep(30000); 25 } 26 catch(InterruptedException e) { 27 } 28 29 timer.cancel(); 30 System.out.println("End testSchedule:" + new Date() + "\n"); 31 } 32 33 void testScheduleAtFixedRate(){ 34 35 Timer timer = new Timer(); 36 System.out.println("In testScheduleAtFixedRate:" + new Date() 37 ); 38 System.out.println("Delay:5秒, Period:5秒"); 39 40 // scheduleAtFixedRate(TimerTask task, long delay, long period) 41 timer.scheduleAtFixedRate(new DateTaskSleep4to8s(), new Date(), 42 5000); 43 44 try { 45 Thread.sleep(30000); 46 } 47 catch(InterruptedException e) { 48 } 49 50 timer.cancel(); 51 System.out.println("End testScheduleAtFixedRate:" + new Date( 52 ) + "\n"); 53 } 54 }執行結果:
首先來看 schedule 的部份,由下面的資料我們可以發現,
當工作執行時間超過 5 秒時,下次的預訂執行時間會以工作結束的時間來計算,
如下面標紅色部份,工作執行了 7秒,則下次的預訂時間就晚 7秒,
而若工作時間少於 5 秒,下次執行的間隔仍維持 5 秒,如下面標藍色部份。
對 schedule 而言,所有預訂和實際執行時間都是相同的,沒有 catch up 的情況。
In testSchedule:Sun Dec 25 14:56:31 CST 2011 Delay:5秒, Period:5秒 Task 預訂執行時間:Sun Dec 25 14:56:31 CST 2011, 實際執行時間:Sun Dec 25 14:56:31 CST 2011 Task going to sleep 5s. Task 預訂執行時間:Sun Dec 25 14:56:36 CST 2011, 實際執行時間:Sun Dec 25 14:56:36 CST 2011 Task going to sleep 7s. Task 預訂執行時間:Sun Dec 25 14:56:43 CST 2011, 實際執行時間:Sun Dec 25 14:56:43 CST 2011 Task going to sleep 5s. Task 預訂執行時間:Sun Dec 25 14:56:48 CST 2011, 實際執行時間:Sun Dec 25 14:56:48 CST 2011 Task going to sleep 6s. Task 預訂執行時間:Sun Dec 25 14:56:54 CST 2011, 實際執行時間:Sun Dec 25 14:56:54 CST 2011 Task going to sleep 4s. Task 預訂執行時間:Sun Dec 25 14:56:59 CST 2011, 實際執行時間:Sun Dec 25 14:56:59 CST 2011 Task going to sleep 5s. End testSchedule:Sun Dec 25 14:57:01 CST 2011而對 scheduleAtFixedRate 來說,
當工作執行時間超過 5 秒時,下次的預訂執行時間仍以工作開始的時間來計算,
所以所有後續工作的預訂時間都是間隔 5秒,然而排程的工作不會同時執行,
故雖然預訂時間間隔5秒,但實際執行時間會被 delay,如下面標紅色部份,
工作執行了 6秒,則下次的預訂時間是5秒後,但實際執行是6秒後。
這個預訂時間和實際執行時間的差距,將在後續的工作排程中產生影響,
在實際執行時間落後預訂時間時,scheduleAtFixedRate 會有 catch up 的機制,
在後續若執行時間較短,我們就可以發現執行的間距小於 5秒,如藍色所示。
In testScheduleAtFixedRate:Sun Dec 25 14:57:01 CST 2011 Delay:5秒, Period:5秒 Task 預訂執行時間:Sun Dec 25 14:57:01 CST 2011, 實際執行時間:Sun Dec 25 14:57:01 CST 2011 Task going to sleep 6s. Task 預訂執行時間:Sun Dec 25 14:57:06 CST 2011, 實際執行時間:Sun Dec 25 14:57:07 CST 2011 Task going to sleep 5s. Task 預訂執行時間:Sun Dec 25 14:57:11 CST 2011, 實際執行時間:Sun Dec 25 14:57:12 CST 2011 Task going to sleep 5s. Task 預訂執行時間:Sun Dec 25 14:57:16 CST 2011, 實際執行時間:Sun Dec 25 14:57:17 CST 2011 Task going to sleep 6s. Task 預訂執行時間:Sun Dec 25 14:57:21 CST 2011, 實際執行時間:Sun Dec 25 14:57:23 CST 2011 Task going to sleep 4s. Task 預訂執行時間:Sun Dec 25 14:57:26 CST 2011, 實際執行時間:Sun Dec 25 14:57:27 CST 2011 Task going to sleep 6s. End testScheduleAtFixedRate:Sun Dec 25 14:57:31 CST 2011看完了以上的例子,我們發現 schedule 和 scheduleAtFixedRate都不會同時執行,
而是接續著執行,所以不須考慮同步的問題。
這點在 Timer的schedule和scheduleAtFixedRate方法的区别解析 的解釋是錯的,
因為他印出的都是 scheduledExecutionTime,而非實際執行時間。
接下來我們再來看看若預訂執行的時間已經過了,兩個不同 method 有何差異,
這也會展示 catch up 的影響到底有多大:
這次我們不再需要不同的工作時間,所以工作的程式碼簡化如下:
1 package werdna1222coldcodes.blogspot.com.demo.timer; 2 3 import java.util.*; 4 5 public class DateTaskWithBothTime extends TimerTask { 6 7 @ Override 8 public void run() { 9 System.out.println("Task 預訂執行時間:" 10 + new Date(this.scheduledExecutionTime()) 11 + ", \n實際執行時間:" + new Date()); 12 } 13 }而測試的程式碼如下:
1 package werdna1222coldcodes.blogspot.com.demo.timer; 2 3 import java.util.*; 4 5 public class TimerScheduleAndScheduleAtFixedRateDemo { 6 public static void main(String[] args) { 7 8 TimerScheduleAndScheduleAtFixedRateDemo timerDemo 9 = new TimerScheduleAndScheduleAtFixedRateDemo(); 10 11 timerDemo.testSchedulePassedDate(); 12 timerDemo.testScheduleAtFixedRatePassedDate(); 13 } 14 15 void testSchedulePassedDate(){ 16 17 Timer timer = new Timer(); 18 System.out.println("In testSchedulePassedDate:" + new Date()) 19 ; 20 System.out.println("Period:5秒"); 21 22 // 設定填入schedule中的 Date firstTime 23 為現在的15秒前 24 Calendar calendar = Calendar.getInstance(); 25 calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND)-15) 26 ; 27 Date firstTime = calendar.getTime(); 28 29 // 也可用 simpleDateFormat 直接設定 firstTime 30 的精確時間 31 // SimpleDateFormat dateFormatter = 32 // new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); 33 // Date firstTime = dateFormatter.parse("2011/12/25 13:30:00"); 34 35 // schedule(TimerTask task, Date firstTime, long period) 36 timer.schedule(new DateTaskWithBothTime(), firstTime, 5000); 37 38 try { 39 Thread.sleep(30000); 40 } 41 catch(InterruptedException e) { 42 } 43 44 timer.cancel(); 45 System.out.println("End testSchedulePassedDate:" + new Date() 46 + "\n"); 47 } 48 49 void testScheduleAtFixedRatePassedDate(){ 50 51 Timer timer = new Timer(); 52 System.out.println("In testScheduleAtFixedRatePassedDate:" 53 + new Date()); 54 System.out.println("Period:5秒"); 55 56 // 設定填入schedule中的 Date firstTime 57 為現在的15秒前 58 Calendar calendar = Calendar.getInstance(); 59 calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND)-15) 60 ; 61 Date firstTime = calendar.getTime(); 62 63 // schedule(TimerTask task, Date firstTime, long period) 64 timer.scheduleAtFixedRate(new DateTaskWithBothTime(), 65 firstTime, 5000); 66 67 try { 68 Thread.sleep(30000); 69 } 70 catch(InterruptedException e) { 71 } 72 73 timer.cancel(); 74 System.out.println("End testScheduleAtFixedRatePassedDate:" 75 + new Date() + "\n"); 76 } 77 }執行的結果為:
In testSchedulePassedDate:Sun Dec 25 16:11:32 CST 2011 Period:5秒 Task 預訂執行時間:Sun Dec 25 16:11:32 CST 2011, 實際執行時間:Sun Dec 25 16:11:32 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:37 CST 2011, 實際執行時間:Sun Dec 25 16:11:37 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:42 CST 2011, 實際執行時間:Sun Dec 25 16:11:42 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:47 CST 2011, 實際執行時間:Sun Dec 25 16:11:47 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:52 CST 2011, 實際執行時間:Sun Dec 25 16:11:52 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:57 CST 2011, 實際執行時間:Sun Dec 25 16:11:57 CST 2011 End testSchedulePassedDate:Sun Dec 25 16:12:02 CST 2011 In testScheduleAtFixedRatePassedDate:Sun Dec 25 16:12:02 CST 2011 Period:5秒 Task 預訂執行時間:Sun Dec 25 16:11:47 CST 2011, 實際執行時間:Sun Dec 25 16:12:02 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:52 CST 2011, 實際執行時間:Sun Dec 25 16:12:02 CST 2011 Task 預訂執行時間:Sun Dec 25 16:11:57 CST 2011, 實際執行時間:Sun Dec 25 16:12:02 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:02 CST 2011, 實際執行時間:Sun Dec 25 16:12:02 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:07 CST 2011, 實際執行時間:Sun Dec 25 16:12:07 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:12 CST 2011, 實際執行時間:Sun Dec 25 16:12:12 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:17 CST 2011, 實際執行時間:Sun Dec 25 16:12:17 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:22 CST 2011, 實際執行時間:Sun Dec 25 16:12:22 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:27 CST 2011, 實際執行時間:Sun Dec 25 16:12:27 CST 2011 Task 預訂執行時間:Sun Dec 25 16:12:32 CST 2011, 實際執行時間:Sun Dec 25 16:12:32 CST 2011 End testScheduleAtFixedRatePassedDate:Sun Dec 25 16:12:32 CST 2011由上面的結果我們可以發現當現在的時間已經超過指定的 date 時,
schedule 會直接從現在開始做,且沒有 catch up 的情況。
而 scheduleAtFixedRate 則會發生 catch up,即他會想要趕上預訂的執行時間,
所以已過期的任務仍會被執行,這也是為何藍色部份會在一開始被執行連續被執行。
從以上所有的測試結果來看,兩者主要的差異有兩點:
- 執行工作時間較預訂工作間隔長時:
schedule 會直接 delay 後續的工作預訂的時間;
scheduleAtFixedRate 後續工作的預訂時間仍按工作間隔計算,
後續若有工作提早完成,會以 catch up 來追上預訂時間。 - 執行任務已過期時:
若只執行單次不repeat,則兩者都不會執行;但若有 repeat,
schedule 不會管前面過期的任務,直接由當下開始執行並計算後續的工作時間;
scheduleAtFixedRate 則一樣以 catch up 機制,會先補足前面未完成的部份。
http://kickjava.com/src/java/util/Timer.java.htm
若想知道更多有關 Java 時間相關的轉換、排程等應用,請見:
Java 時間日期處理範例大全:含時間單位格式轉換、期間計算、排程等
關鍵字:java, timer, schedule, scheduleAtFixedRate, 差異, 不同, 比較, 差別, example, 例子, 範例,
參考資料: