Spring Boot 2.X 定时器

举报
福州司马懿 发表于 2021/11/19 06:29:26 2021/11/19
【摘要】 三种定时方案 Java自带的 timer 类,可以实现指定频率的任务调度,通过 timer.schedule() 启动,timer.cancel() 终止。采用 Quartz 调度器实现。这是一个开源的...

三种定时方案

  • Java自带的 timer 类,可以实现指定频率的任务调度,通过 timer.schedule() 启动,timer.cancel() 终止。
  • 采用 Quartz 调度器实现。这是一个开源的专门用于定时任务调度的框架,就是有点复杂
  • spring3.0后自带 ThreadPoolTaskScheduler,可以实现 Quartz 的大部分功能,且不需要额外引用 jar,支持注解和配置文件两种方式。

1. Timer

timer 是单线程执行,每当上一个任务执行完毕之后,才进行下一个判断。
一旦中途抛出一个异常,且未能被捕获,则整个定时器终止。

两个调度函数

  • schedule 调度一个task,在delay(ms)后开始调度,每次任务执行完后,至少等待period(ms)后才开始调度
public void schedule(TimerTask task, Date time)
public void schedule(TimerTask task, long delay)
public void schedule(TimerTask task, Date firstTime, long period)
public void schedule(TimerTask task, long delay, long period)

  
 
  • 1
  • 2
  • 3
  • 4

In fixed-delay execution, each execution is scheduled relative tothe actual execution time of the previous execution. If an executionis delayed for any reason (such as garbage collection or otherbackground activity), subsequent executions will be delayed as well.In the long run, the frequency of execution will generally be slightlylower than the reciprocal of the specified period (assuming the systemclock underlying Object.wait(long) is accurate). As aconsequence of the above, if the scheduled first time is in the past,it is scheduled for immediate execution.

在固定延迟执行中,每次执行都是相对于上一次执行的实际执行时间进行调度的。如果由于任何原因(如垃圾收集或其他后台活动)导致执行延迟,则后续执行也将延迟。执行频率通常略低于指定周期的倒数(我们假设 Object.wait(long) 所依据的系统时钟是准确的)。综上所述,如果第一次预计执行的时间已经过了,那么它会立即执行。

  • scheduleAtFixedRate 调度一个task,在delay(ms)后开始调度,至少 等待 period(ms)调用一次(与任务执行耗时无关)
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
public void scheduleAtFixedRate(TimerTask task, long delay, long period)

  
 
  • 1
  • 2

In fixed-rate execution, each execution is scheduled relative to thescheduled execution time of the initial execution. If an execution isdelayed for any reason (such as garbage collection or other backgroundactivity), two or more executions will occur in rapid succession to"catch up." In the long run, the frequency of execution will beexactly the reciprocal of the specified period (assuming the systemclock underlying Object.wait(long) is accurate).

在固定速率执行中,每次执行都是相对于初始执行的预定执行时间进行调度的。如果执行因任何原因而延迟(例如垃圾收集或其他后台活动),则会连续快速执行两次或更多次以“赶上”。从长远来看,执行频率将会是周期的倒数(我们假设 Object.wait(long) 所依据的系统时钟是准确的)。

两种使用方式

  • 一个 timer 调度多个任务
    当执行时间小于 period 时,两个函数表现一致,会在每次任务执行完毕后,判断从调度开始到现在经过的时间是否大于 period,如果是则继续调度,否则等待时间到了 period 才再次调度。
    如果执行时间大于 period,scheduleAtFixedRate 的优先级大于schedule。

  • 一个 timer 仅调度一个任务
    两个函数表现一致,都是在每次任务执行完毕后,判断从调度开始到现在经过的时间是否大于 period,如果是则继续调度,否则等待时间到了 period 才再次调度。

注意:由于不是外部因素导致的延迟,因此在如下两种调度方式中, scheduleAtFixedRate 均不会有追赶的现象。

终止定时器

  • timer.cancel() 终止定时器
  • timerTask.cancel() 终止定时器中的某个任务
  • timer.purge() 从计时器的任务队列中移除所有已取消的任务,并返回移除的个数

测试用例

  • 一个 timer 调度多个任务
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss.SSS");
//调度一个task,在delay(ms)后开始调度,每次调度完后,最少等待period(ms)后才开始调度
long delay = 1000;
long period = 2000;
long cost = 3000;
Timer timer = new Timer();
timer.schedule(new TimerTask() {

		@Override
		public void run() {
			System.out.println("schedule: " + sdf.format(new Date()));
			try {
				Thread.sleep(cost);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("schedule end ! ! !");
		}
		
	}, delay, period);
	
	timer.scheduleAtFixedRate(new TimerTask() {

		@Override
		public void run() {
			System.out.println("scheduleAtFixedRate: " + sdf.format(new Date()));
			try {
				Thread.sleep(cost);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("scheduleAtFixedRate end …… ……");
		}
		
	}, delay, period);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

(1)period = 2s,cost = 3s 的情况
在这里插入图片描述
(2)period = 2s,cost = 1s 的情况
在这里插入图片描述

  • 一个 timer 调度一个任务
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss.SSS");
//调度一个task,在delay(ms)后开始调度,每次调度完后,最少等待period(ms)后才开始调度
long delay = 1000;
long period = 2000;
long cost = 1000;
Timer timer1 = new Timer();
Timer timer2 = new Timer();
timer1.schedule(new TimerTask() {

	@Override
	public void run() {
		System.out.println("schedule: " + sdf.format(new Date()));
		try {
			Thread.sleep(cost);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}, delay, period);

timer2.scheduleAtFixedRate(new TimerTask() {

	@Override
	public void run() {
		System.out.println("scheduleAtFixedRate: " + sdf.format(new Date()));
		try {
			Thread.sleep(cost);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}, delay, period);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

(1)period = 2s,cost = 3s 的情况
在这里插入图片描述
(2)period = 2s,cost = 1s 的情况
在这里插入图片描述

2. Quartz

要使用定时任务需要在启动类(@SpringBootApplication)上,加上启用定时任务的注解(@EnableScheduling)。然后在组件类(@Component)的待执行的方法上加上执行规则注解(@Scheduled)。程序会根据 @Scheduled 所提供的信息定时执行该方法。

Scheduled 参数

参数名 含义
cron 表达式
zone 时区,默认为本地时区 TimeZone.getDefault()
fixedDelay 上次任务执行完后 N 秒继续执行
fixedDelayString
fixedRate 每 N 秒执行一次
fixedRateString
initialDelay 初始延迟 N 秒后执行
initialDelayString

在线 Cron 表达式生成器

http://cron.qqe2.com/

cron 规则

执行定时任务

@Component
@EnableScheduling
public class ScheduleTask {

	private static final SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
	
	private String getTime() {
		return sdf.format(new Date());
	}
	
	/** 从第0秒开始,每隔5秒执行一次 */
	@Scheduled(cron = "0/5 * * * * *")
	public void run1() {
		System.out.println(getTime() + " cron");
	}

	/** 立即执行,然后每隔3秒执行一次 */
	@Scheduled(fixedRate = 3000)
	public void run2() {
		System.out.println(getTime() + " fixedRate = 3000");
	}

	/** 立即执行,然后在上次执行完毕后,过2秒再次执行 */
	@Scheduled(fixedDelay = 2000)
	public void run3() {
		System.out.println(getTime() + " fixedDelay = 2000");
	}

	/** 初始时延迟1秒后执行,后续每次都在上一次执行完3秒后再执行 */
	@Scheduled(initialDelay = 1000, fixedDelay = 3000)
	public void run4() {
		System.out.println(getTime() + " initialDelay = 1000, fixedDelay = 3000");
	}
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

关闭定时器

3. ThreadPoolTaskScheduler

它可以使用 cron 表达式,因此能实现 Quartz 的所有功能。
和 Timer 的区别是

  • ThreadPoolTaskScheduler 是基于多线程的,而 Timer 是基于单线程的。
  • 当有其中一个任务,有未捕获的错误被抛出时,因为 Timer 是单线程的,所以会导致整个定时器终止;而对 ThreadPoolTaskScheduler 的影响,仅仅是该任务的本次执行终止而已。
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss.SSS");
		
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
//设置线程池大小
scheduler.setPoolSize(10);
//设置线程名前缀
      scheduler.setThreadNamePrefix("task-");
      //设置关闭前最多等几秒
      scheduler.setAwaitTerminationSeconds(10);
      //设置等待任务执行完毕后再关闭
      scheduler.setWaitForTasksToCompleteOnShutdown(true);
      
ScheduledFuture<?> future = scheduler.schedule(()->{
	System.out.println("ThreadPoolTaskScheduler: " + sdf.format(new Date()));
	throw new RuntimeException("");
	
}, 
//cron表达式的含义是,2秒执行一次
new CronTrigger("0/2 * * * * ? "));

//是否可以中断正在执行的任务
//boolean mayInterruptIfRunning = true;
//终止任务
//future.cancel(mayInterruptIfRunning);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

文章来源: blog.csdn.net,作者:福州-司马懿,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/chy555chy/article/details/100031025

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。