2018-04-10-java-Timer中schedule和scheduleAtFixedRate的区别

schedule和scheduleAtFixedRate的区别

固定延迟和固定速率

schedule属于固定延迟的,scheduleAtFixedRate属于固定速率的。

下面用T、W、G来演示这二者的区别

  • 一个T代表执行Task1秒
  • 一个W表示空闲1秒
  • 一个G表示GC回收执行1秒

TTWWWTTWWWTTWWW 好看一点: TTWWW TTWWW TTWWW, 那么这一段就代表一个任务执行耗时2秒,它的period被设置为5秒,因为从开始执行到下个任务开始执行,有2个T,3个W,5秒一轮回。

任务周期 > 任务执行所需时间

在schedule中,因为固定延迟,若任务二因为GC被推迟了2秒开始执行,那么任务三是参考任务二开始的时间+5秒的延迟。它是这样的: TTWWW GG TTWWW TTWWW

In fixed-delay execution, each execution is scheduled relative to the actual execution time of the previous execution. If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well.

在scheduleAtFixRate中,因为固定速率,他的速率是参照第一个,所以当其中一个任务因为任何原因被延迟了,后续的任务会进行追赶。它是这样的: TTWWW GG TTW TTWWW

In fixed-rate execution, each execution is scheduled relative to the scheduled execution time of the initial execution. If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to “catch up.”

上面情况都是在 任务周期 > 任务执行所需时间,如任务周期为5S,运行需要2S。可以很清晰的看出二者区别。

任务周期 <= 任务执行所需时间

如果任务周期 < 任务执行所需时间,如任务周期为2S,运行需要5S,这个时候schedule和scheduleAtFixedRate(对于固定速率的任务来说根本没多余的时间来赶)的表现都是一样的,都是上个任务执行完马上执行下个任务。它是这样的:TTTTT TTTTT TTTTT

追赶性

由于scheduleAtFixedRate具有追赶性,如果一个任务的firstTime(第一次执行时间)被设置在程序运行时间之前,那么它也会开始追赶。

如下demo:任务执行需要1秒,周期为5秒,把任务开始时间设置在程序运行的1分钟之前:

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

public class TimerTest2 {

	final private static SimpleDateFormat sdf = new SimpleDateFormat("HH时mm分ss秒");
	public static void main(String[] args) {
		
		Calendar calendar = Calendar.getInstance();
		System.out.println("程序启动时间:" + sdf.format(calendar.getTime()));
		
		Timer timer = new Timer();
		List<TimerTask> tts = new ArrayList<TimerTask>();
		
		final int taskNumber = 1;
		for (int i = 0; i < taskNumber; i++) {
			final int finalI = i;
			tts.add(new TimerTask() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName() + ":任务" + finalI + "开始时间:" + sdf.format(new Date()));
					try {
						Thread.sleep(1 * 1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + ":任务" + finalI + "结束时间:" + sdf.format(new Date()));
				}
			});
		}
        for (int i = 0; i < taskNumber; i++) {
            calendar.add(Calendar.MINUTE, -1); /* 1分钟前 */
			timer.scheduleAtFixedRate(tts.get(i), calendar.getTime(), 5 * 1000);
        }
    }
}

如下运行结果及注释:

程序启动时间:200143Timer-0:任务0开始时间:200143// 1S正常
Timer-0:任务0结束时间:200144Timer-0:任务0开始时间:200144// 利用空闲时间来追赶第1次
Timer-0:任务0结束时间:200145Timer-0:任务0开始时间:200145// 空闲追赶2
Timer-0:任务0结束时间:200146Timer-0:任务0开始时间:200146// 空闲追赶3
Timer-0:任务0结束时间:200147Timer-0:任务0开始时间:200147// 空闲追赶4
Timer-0:任务0结束时间:200148Timer-0:任务0开始时间:200148// 1S正常
Timer-0:任务0结束时间:200149Timer-0:任务0开始时间:200149// 空闲追赶5
Timer-0:任务0结束时间:200150Timer-0:任务0开始时间:200150// 空闲追赶6
Timer-0:任务0结束时间:200151Timer-0:任务0开始时间:200151// 空闲追赶7
Timer-0:任务0结束时间:200152Timer-0:任务0开始时间:200152// 空闲追赶8
Timer-0:任务0结束时间:200153Timer-0:任务0开始时间:200153// 1S正常
Timer-0:任务0结束时间:200154Timer-0:任务0开始时间:200154// 空闲追赶9
Timer-0:任务0结束时间:200155Timer-0:任务0开始时间:200155// 空闲追赶10
Timer-0:任务0结束时间:200156Timer-0:任务0开始时间:200156// 空闲追赶11
Timer-0:任务0结束时间:200157Timer-0:任务0开始时间:200157// 空闲追赶12
Timer-0:任务0结束时间:200158Timer-0:任务0开始时间:200158// 追赶完毕,此时开始正常执行
Timer-0:任务0结束时间:200159Timer-0:任务0开始时间:200203Timer-0:任务0结束时间:200204Timer-0:任务0开始时间:200208Timer-0:任务0结束时间:200209Timer-0:任务0开始时间:200213Timer-0:任务0结束时间:200214...    

scheduleAtFixedRate为了追赶这1分钟的差距,它会在上个任务执行完马上执行下个任务。任务周期为5S,由于任务开始时间时在1分钟之前,所以很容易算出为了追上要多执行12次(60S / 5S = 12次)。

程序从20时01分43秒启动,周期为5S,实际任务中只运行1S(因为线程只sleep 1秒),那么剩余的4S(周期5S-运行1S)可以用来追赶前面1分钟的没做的那12次,那12次任务,每个需要1S。

  • 1S正常任务 + 4S追赶(可以追赶4个)
  • 1S正常任务 + 4S追赶(可以追赶4个)
  • 1S正常任务 + 4S追赶(可以追赶4个)
  • 当第15秒执行完毕时时,发现12个任务全部追赶完毕,所以第16秒开始就正常执行,不追赶了。

如果我们使用schedule而不是scheduleAtFixedRate,那么是不会去追赶前面少掉的次数的。使用schedule的结果如下:

程序启动时间:202303Timer-0:任务0开始时间:202303Timer-0:任务0结束时间:202304Timer-0:任务0开始时间:202308Timer-0:任务0结束时间:202309Timer-0:任务0开始时间:202313Timer-0:任务0结束时间:202314Timer-0:任务0开始时间:202318Timer-0:任务0结束时间:202319Timer-0:任务0开始时间:202323...

总结

  • 在不需要追赶的情况下,schedule和scheduleAtFixedRate的表现一样。
  • 在需要追赶的情况下(由于GC延迟,或者任务启动时间为之前的时间)
    • 如果任务执行的时间没有超过任务周期,那么scheduleAtFixedRate会去追赶任务,schedule不会追赶。
    • 如果任务执行的时间超过任务周期,二者的表现也是一样的,因为scheduleAtFixedRated没有多余的时间去追赶。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值