Java Timer:schedule和scheduleAtFixedRate

schedule和scheduleAtFixedRate的区别:

  1. schedule為「fixed-delay」,執行時間參照前次工作執行完成的時間:
    若執行工作沒被 delay,則按照預訂的時間執行;但若執行工作時被 delay了,
    随後工作的預訂執行時間會按照上一次執行「完成」的時間點來計算
  2. scheduleAtFixedRate為「fixed-rate」,執行時間參照一開始的時間點;
    和schedule一樣,若執行工作沒被 delay,則按照預訂的時間執行;
    但如果執行工作時被delay了,
    後續的預訂執行時間仍按照上一次執行「開始」的時間點計算,
    為了「catch up」預訂時間,會連續多次補足未完成的任務

另外在在單次執行的情況下,
只要預訂要執行的時間已經過了,那麼兩者都會直接把工作移除不執行。
但若在有設定 period 的情況下,若預訂要執行的時間已經過了,
基於上面所描述的行為,schedule 沒有上次執行完成的時間,會從現在開始算並執行,
而 scheduleAtFixedRate 則以預訂執行的時間開始算,且會一口氣將過去未做的補上!

 

下面是工作 Task 的程式碼,每次進到工作時會 random 睡上 4~8秒才結束。

 

class DelayTimer extends TimerTask {
		public void run() {
			System.out.println("-------------DelayTimer开始-------------------------");
			System.out.println("Task 预定开始时间:"
					+ dateFormat(new Date(this.scheduledExecutionTime()), "yyyy-MM-dd HH:mm:ss") + ", \n实际开始时间:"
					+ dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
			try {
				int sleepSeconds = (int) (4 + Math.random() * 4);
				System.out.println("Task going to sleep " + sleepSeconds + "s.");
				Thread.sleep(sleepSeconds * 1000);
			} catch (InterruptedException e) {
			}
			System.out.println("-------------DelayTimer结束-------------------------");
		}
	}

 

 

下面則是測試程式的程式碼,預訂的時間間隔為 5秒:

 

void testSchedule() {

		Timer timer = new Timer();
		System.out.println("In testSchedule:" +dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
		System.out.println("Delay:5秒, Period:5秒");

		// schedule(TimerTask task, long delay, long period)
		timer.schedule(new DelayTimer(), new Date(), 5000);

		try {
			Thread.sleep(30000);
		} catch (InterruptedException e) {
		}

		timer.cancel();
		System.out.println("End testSchedule:" + dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss") + "\n");
	}
	
	void testScheduleAtFixedRate() {

		Timer timer = new Timer();
		System.out.println("In testScheduleAtFixedRate:" + dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
		System.out.println("Delay:5秒, Period:5秒");

		// scheduleAtFixedRate(TimerTask task, long delay, long period)
		timer.scheduleAtFixedRate(new DelayTimer(), new Date(), 5000);

		try {
			Thread.sleep(30000);
		} catch (InterruptedException e) {
		}

		timer.cancel();
		System.out.println("End testScheduleAtFixedRate:" + dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss") + "\n");
	}

	 String dateFormat(Date date, String format) {
		String result = null;
		try {
			if (date == null) {
				result = "";
			} else {
				SimpleDateFormat sdf = new SimpleDateFormat(format);
				result = sdf.format(date).toString();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

 執行結果:
首先來看 schedule 的部份,由下面的資料我們可以發現,
當工作執行時間超過 5 秒時,下次的預訂執行時間會以工作結束的時間來計算,
如下面標紅色部份,工作執行了 7秒,則下次的預訂時間就晚 7秒,
而若工作時間少於 5 秒,下次執行的間隔仍維持 5 秒,如下面標藍色部份。
對 schedule 而言,所有預訂和實際執行時間都是相同的,沒有 catch up 的情況。

 

 

 In testSchedule:2014-09-04 09:06:04
Delay:5秒, Period:5秒
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:04,
实际开始时间:2014-09-04 09:06:04
Task going to sleep 7s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:11,
实际开始时间:2014-09-04 09:06:11
Task going to sleep 5s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:16,
实际开始时间:2014-09-04 09:06:16
Task going to sleep 4s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:21,
实际开始时间:2014-09-04 09:06:21
Task going to sleep 6s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:27,
实际开始时间:2014-09-04 09:06:27
Task going to sleep 6s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:33,
实际开始时间:2014-09-04 09:06:33
Task going to sleep 5s.
End testSchedule:2014-09-04 09:06:34

 


而對 scheduleAtFixedRate 來說,
當工作執行時間超過 5 秒時,下次的預訂執行時間仍以工作開始的時間來計算,
所以所有後續工作的預訂時間都是間隔 5秒,然而排程的工作不會同時執行
故雖然預訂時間間隔5秒,但實際執行時間會被 delay,如下面標紅色部份,
工作執行了 6秒,則下次的預訂時間是5秒後,但實際執行是6秒後。
這個預訂時間和實際執行時間的差距,將在後續的工作排程中產生影響,
在實際執行時間落後預訂時間時,scheduleAtFixedRate 會有 catch up 的機制,
在後續若執行時間較短,我們就可以發現執行的間距小於 5秒,如藍色所示。

 

In testScheduleAtFixedRate:2014-09-04 09:06:34
Delay:5秒, Period:5秒
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:34,
实际开始时间:2014-09-04 09:06:34
Task going to sleep 4s.
-------------DelayTimer结束-------------------------
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:39,
实际开始时间:2014-09-04 09:06:39
Task going to sleep 7s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:44,
实际开始时间:2014-09-04 09:06:46
Task going to sleep 7s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:49,
实际开始时间:2014-09-04 09:06:53
Task going to sleep 6s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:54,
实际开始时间:2014-09-04 09:06:59
Task going to sleep 4s.
-------------DelayTimer结束-------------------------
-------------DelayTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:06:59,
实际开始时间:2014-09-04 09:07:03
Task going to sleep 5s.
End testScheduleAtFixedRate:2014-09-04 09:07:04

-------------DelayTimer结束-------------------------

 

看完了以上的例子,我們發現 schedule 和 scheduleAtFixedRate都不會同時執行,
而是接續著執行,所以不須考慮同步的問題。

 

接下來我們再來看看若預訂執行的時間已經過了,兩個不同 method 有何差異,
這也會展示 catch up 的影響到底有多大:
這次我們不再需要不同的工作時間,所以工作的程式碼簡化如下:

class NormalTimer extends TimerTask {
		public void run() {
			System.out.println("-------------NormalTimer开始-------------------------");
			System.out.println("Task 预定开始时间:"
					+ dateFormat(new Date(this.scheduledExecutionTime()), "yyyy-MM-dd HH:mm:ss") + ", \n实际开始时间:"
					+ dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));			
			System.out.println("-------------NormalTimer结束-------------------------");
		}
}

 测试代码如下:

void testSchedulePassedDate() {

		Timer timer = new Timer();
		System.out.println("In testSchedule:" +dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
		System.out.println("Period:5秒");
		
		// 設定填入schedule中的 Date firstTime 
		//         為現在的15秒前
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND)-15);
        Date firstTime = calendar.getTime();

		// schedule(TimerTask task, Date firstTime, long period)
		timer.schedule(new NormalTimer(), firstTime, 5000);

		try {
			Thread.sleep(30000);
		} catch (InterruptedException e) {
		}

		timer.cancel();
		System.out.println("End testSchedule:" + dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss") + "\n");
	}
	
	void testScheduleAtFixedRatePassedDate() {

		Timer timer = new Timer();
		System.out.println("In testScheduleAtFixedRate:" + dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
		System.out.println("Period:5秒");
		
		// 設定填入schedule中的 Date firstTime 
		//         為現在的15秒前
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND)-15);
        Date firstTime = calendar.getTime();

		// scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
		timer.scheduleAtFixedRate(new NormalTimer(), firstTime, 5000);

		try {
			Thread.sleep(30000);
		} catch (InterruptedException e) {
		}

		timer.cancel();
		System.out.println("End testScheduleAtFixedRate:" + dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss") + "\n");
}

 執行的結果為:

In testSchedule:2014-09-04 09:31:40
Period:5秒
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:31:40,
实际开始时间:2014-09-04 09:31:40
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:31:45,
实际开始时间:2014-09-04 09:31:45
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:31:50,
实际开始时间:2014-09-04 09:31:50
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:31:55,
实际开始时间:2014-09-04 09:31:55
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:00,
实际开始时间:2014-09-04 09:32:00
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:05,
实际开始时间:2014-09-04 09:32:05
-------------NormalTimer结束-------------------------
End testSchedule:2014-09-04 09:32:10

In testScheduleAtFixedRate:2014-09-04 09:32:10
Period:5秒
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:31:55,
实际开始时间:2014-09-04 09:32:10
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:00,
实际开始时间:2014-09-04 09:32:10
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:05,
实际开始时间:2014-09-04 09:32:10
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:10,
实际开始时间:2014-09-04 09:32:10
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:15,
实际开始时间:2014-09-04 09:32:15
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:20,
实际开始时间:2014-09-04 09:32:20
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:25,
实际开始时间:2014-09-04 09:32:25
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:30,
实际开始时间:2014-09-04 09:32:30
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:35,
实际开始时间:2014-09-04 09:32:35
-------------NormalTimer结束-------------------------
-------------NormalTimer开始-------------------------
Task 预定开始时间:2014-09-04 09:32:40,
实际开始时间:2014-09-04 09:32:40
-------------NormalTimer结束-------------------------
End testScheduleAtFixedRate:2014-09-04 09:32:40

 

由上面的結果我們可以發現當現在的時間已經超過指定的 date 時,
schedule 會直接從現在開始做,且沒有 catch up 的情況。
而 scheduleAtFixedRate 則會發生 catch up,即他會想要趕上預訂的執行時間,
所以已過期的任務仍會被執行,這也是為何藍色部份會在一開始被執行連續被執行。

從以上所有的測試結果來看,兩者主要的差異有兩點:

  1. 執行工作時間較預訂工作間隔長時:
    schedule 會直接 delay 後續的工作預訂的時間;
    scheduleAtFixedRate 後續工作的預訂時間仍按工作間隔計算,
    後續若有工作提早完成,會以 catch up 來追上預訂時間。
  2. 執行任務已過期時:
    若只執行單次不repeat,則兩者都不會執行;但若有 repeat,
    schedule 不會管前面過期的任務,直接由當下開始執行並計算後續的工作時間;
    scheduleAtFixedRate 則一樣以 catch up 機制,會先補足前面未完成的部份。

本文参考自  http://www.ewdna.com/2011/12/java-timerschedulescheduleatfixedrate.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值