定时任务
就是基于给定的时间点、给定的时间间隔、给定的执行次数自动执行的任务。
1.使用SpringBoot 注解(@Scheduled)创建定时任务(常用)
基于注解(@Scheduled),实现定时任务
BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanPostProcessor {
//bean初始化方法调用前被调用
Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;
//bean初始化方法调用后被调用
Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String cron() default "";
String zone() default "";
long fixedDelay() default -1L;
String fixedDelayString() default "";
long fixedRate() default -1L;
String fixedRateString() default "";
long initialDelay() default -1L;
String initialDelayString() default "";
}
参数解释:
1.Cron表达式参数分别表示:(日期和星期可以使用”?”来实现互斥)
秒(0~59) 例如0/5表示每5秒
分(0~59)
时(0~23)
日(0~31)
月(0~11)
周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
00 02 * * * ls #每天的凌晨2点整执行
00 02 1 * * ls #每月的1日的凌晨2点整执行
00 02 14 2 * ls #每年的2月14日凌晨2点执行
00 02 * * 7 ls #每周天的凌晨2点整执行
00 02 * 6 5 ls #每年的6月周五凌晨2点执行
00 02 14 * 7 ls #每月14日或每周日的凌晨2点都执行
00 02 14 2 7 ls #每年的2月14日或每年2月的周天的凌晨2点执行
*/10 02 * * * ls #每天凌晨2点,每隔10分钟执行一次
0 * * * * ls #每分钟都执行
00 00 14 2 * ls #每年2月14日的凌晨执行命令
*/5 * * * * ls #每隔5分钟执行一次
00 02 * 1,5,8 * ls #每年的1月5月8月凌晨2点执行
00 02 1-8 * * ls #每月1号到8号凌晨2点执行
0 21 * * * ls #每天晚上21:00执行
45 4 1,10,22 * * ls #每月1、10、22日的4:45执行
45 4 1-10 * * ls #每月1到10日的4:45执行
3,15 8-11 */2 * * ls #每隔两天的上午8点到11点的第3和第15分钟执行
0 23-7/1 * * * ls #晚上11点到早上7点之间,每隔一小时执行
15 21 * * 1-5 ls #周一到周五每天晚上21:15执行
2.zone
时区,接收一个java.util.TimeZone#ID。cron表达式会基于该时区解析。默认是一个空字符串,即取服务器所在地的时区。比如我们一般使用的时区Asia/Shanghai。该字段我们一般留空。
3.fixedDelay
上一次执行完毕时间点之后多长时间再执行。如:
@Scheduled(fixedDelayString = "5000") //上一次执行完毕时间点之后5秒再执行
- fixedDelayString
与 fixedDelay 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。如:
@Scheduled(fixedDelayString = "${time.fixedDelay}")
void testFixedDelayString() {
System.out.println("Execute at " + System.currentTimeMillis());
}
5.fixedRate
上一次开始执行时间点之后多长时间再执行。如:
@Scheduled(fixedRate = 5000) //上一次开始执行时间点之后5秒再执行
6.fixedRateString
与 initialDelay 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。
案例:
/**
* 功能描述:定时任务获取 每小时触发
*
* @author:hdh
* @date: 2020/11/2 11:28
*/
@Scheduled(cron = "0 0 * * * ?")
private void getAppToken() {
//获取
Map<String, String> appRes = WeixinUtil.getAppAccessToken();
if (appRes != null) {
this.appletsService.saveAppToken(appRes.get("accessToken"), WEIXIN_WANS_APPID);
}
}
7.基于接口(SchedulingConfigurer),实现定时任务
springBoot起步类必须@EnableScheduling 开启。
实现SchedulingConfigurer接口并且重写configureTasks方法。
scheduledTaskRegistrar的实现类有以下:
@Component
public class TaskSchedulingConfigurer implements SchedulingConfigurer {
private String cron = "* * * * * ?"; //调用set方法可动态设置时间规则
private static Logger logger = LoggerFactory.getLogger(TaskSchedulingConfigurer.class);
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addCronTask(new Runnable() {
@Override
public void run() {
System.out.println("task......");
}
}, cron);
}
}
2.Timer和TimerTask
1.Timer案例
/**
* @description:定时任务
* @author:hdh
* @date:2021-06-04 10:16
**/
public class TimerTask{
public static void main(String[] args) throws Exception {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("schedule");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 2000);
}
}
2.讲解
TimerTask其实就是实现了Runnable接口,通过run()方法可以实现具体的任务。
Timer对于TimerTask来讲就是一个调度器,负责开启,设置,关闭调度任务。
3.Timer一些常见的调度方法
当程序初始化完成Timer后会调度一个task,经过delay(ms)后开始进行调度,每间隔period调度一次。
public void schedule(TimerTask task, long delay, long period)
public void scheduleAtFixedRate(TimerTask task, long delay, long period)
其他的方法基本都类似,主要是schedule和scheduleAtFixedRate之间的区别
对比一下
Timer对象内部有一个TimerThread线程,对象创建后,此线程就开始运行,所有任务都是有此线程调度,而不是每个任务创建一个线程执行。其中mainLoop就是任务调度的核心部分。若period不为0,说明是周期性执行的任务,根据period的正负决定是根据本次实际运行时间还是理论运行时间计算下次执行时间。若每次任务的执行时间都没有被延迟,二者就没有什么区别。当任务某次执行发生了延迟时,二者的区别就真正体现出来了。
schedule()
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
scheduleAtFixedRate()
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
schedule() 与scheduleAtFixedRate() 都是调用了sched()方法唯一不同之处就是period和-period
private void mainLoop() {
...............
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
..........................
}
通过判断period的正负,也就知道了调用的是schedule还是scheduleAtFixedRate从而计算下一次任务的执行时间
task.period<0 ? currentTime - task.period :executionTime + task.period);
而 schedule() 讲究的是如果Timer在调度中,发生了延时,后面的任务会自动延时,也就是说schedule()方法的
下一次任务的执行时间=当前任务执行完成时间+延时间隔时间
实际执行时间:2021 06 22 03:28:02理想执行时间:2021 06 22 03:28:02
实际执行时间:2021 06 22 03:28:03理想执行时间:2021 06 22 03:28:03
实际执行时间:2021 06 22 03:28:04理想执行时间:2021 06 22 03:28:04
================发生意外延时================
实际执行时间:2021 06 22 03:28:10理想执行时间:2021 06 22 03:28:05
实际执行时间:2021 06 22 03:28:10理想执行时间:2021 06 22 03:28:10
实际执行时间:2021 06 22 03:28:11理想执行时间:2021 06 22 03:28:11
实际执行时间:2021 06 22 03:28:12理想执行时间:2021 06 22 03:28:12
那么 scheduleAtFixedRate() 方法则是在开始调度任务是就已经计算好下一次的任务调度时间,即使过了调度时间也会被调用,
实际执行时间:2021 06 22 03:30:56理想执行时间:2021 06 22 03:30:56
实际执行时间:2021 06 22 03:30:57理想执行时间:2021 06 22 03:30:57
实际执行时间:2021 06 22 03:30:58理想执行时间:2021 06 22 03:30:58
================发生意外延时================
实际执行时间:2021 06 22 03:31:04理想执行时间:2021 06 22 03:30:59
实际执行时间:2021 06 22 03:31:04理想执行时间:2021 06 22 03:31:00
实际执行时间:2021 06 22 03:31:04理想执行时间:2021 06 22 03:31:01
实际执行时间:2021 06 22 03:31:04理想执行时间:2021 06 22 03:31:02
实际执行时间:2021 06 22 03:31:04理想执行时间:2021 06 22 03:31:03
实际执行时间:2021 06 22 03:31:04理想执行时间:2021 06 22 03:31:04
实际执行时间:2021 06 22 03:31:05理想执行时间:2021 06 22 03:31:05
实际执行时间:2021 06 22 03:31:06理想执行时间:2021 06 22 03:31:06
实际执行时间:2021 06 22 03:31:07理想执行时间:2021 06 22 03:31:07
这就是他们之间的差距。
3.Quartz框架
Quartz是一个完全由java编写的开源作业调度框架,是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。
例如,已提交的订单30分钟为付款后,系统会自动删除该订单。