持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
了解Java中定时任务的实现
Java中实现定时任务有三种方式:
1.JDK自带的Timer
优点 使用简单方便,属于jdk自带的简单任务调度工具类,如果你的业务逻辑相对简单的话,没必要非得用Quartz等框架,使用Timer是完全可以胜任的。JDK自带的Timer使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,需求不多的话加上线程池可以解决比较简单的实时任务。
缺点也比较明显
- Timer不保证任务执行的十分精确, schedule如果某一次调度时间比较长,那么后面的时间会顺延,进而有可能产生一定的时间差
2.不能动态修改时间,修改时间需要整个项目重启,比较麻烦,所以,适用于不怎么需要修改时间的操作
2.Spring中的Spring Task(生产环境中常见)
Spring Task有两种实现方式:
-
使用@Scheduled(cron表达式) 直接指定特定的cron表达式,(cron表达式可以指定很多东西,可以看看下面这位大佬写的www.jianshu.com/p/b4b8950fb…)
-
基于SchedulingConfigurer注册定时任务
相比之下
-
@Scheduled不支持动态修改定时周期,只能整个项目重启去修改cron表达式 SchedulingConfigurer支持动态修改,所以优势就很明显
-
@Scheduled只能是单线程,而SchedulingConfigurer默认是单线程,可以通过添加线程池,实现多线程下定时任务的运行,所以,我们重点学习基于Spring中SchedulingConfigurer实现多定时任务注册及修改执行周期
3.可以借助第三方的Quartz框架
需要去导第三方的依赖包,菜菜的小编还没学到.
下面我们开始看基于Spring中SchedulingConfigurer实现多定时任务注册及修改执行周期的实现
首先先写一个接口,让所有定时任务都实现他,便于定时任务类代码的整洁与规范。
public interface BaskTask extends Runnable {
//获取执行频率的cron表达式
String getCron();
//执行任务逻辑
void execute();
}
定义一个定时任务类
@Slf4j
@Component
@Data
public class TaskOne implements BaskTask {
private String cron;
//外部获取执行频率
@Override
public String getCron() {
return cron;
}
@Override
public void execute() {
//可写复杂的任务逻辑
log.info("taskOne在"+ formatDate(new Date()) +"执行了")
}
//执行任务
@Override
public void run() {
execute();
}
}
最重要的配置部分
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import java.util.Date;
import java.util.Map;
/**
* 定时任务类
* @author Ethereal
* Date:2022/9/26 10:34
*/
/*
@EnableScheduling
@Slf4j
@Configuration
public class TaskConfig implements SchedulingConfigurer {
@Autowired
private ApplicationContext applicationContext;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//获取所有的定时任务
Map<String, BaskTask> map = applicationContext.getBeansOfType(BaskTask.class);
//遍历注册
for(String key :map.keySet()){
BaskTask baskTask = map.get(key);
//baskTask:回调内部的run方法
//baskTask.getCron() 获取各个任务的执行频率
scheduledTaskRegistrar.addTriggerTask(baskTask,sheduledConfig->{
Date date = new CronTrigger(baskTask.getCron()).nextExecutionTime(sheduledConfig);
return date;
});
}
}
}
注意几个地方
-
添加@EnableScheduling注解,使得当前类支持定时任务
-
实现SchedulingConfigurer接口,重写configureTasks方法
-
TaskOne添加了 @Data和字段cron,我们可以用一个接口把cron表达式替换掉,但是注意,需要在原先cron表达式作用下的逻辑运行到才会重新加载生效,所以不会马上修改当前任务,还会按照以前的业务逻辑执行之后再修改,也带来了一定的弊端。
private TaskOne taskOne;
//修改第一个任务提醒时间
@PostMapping("/updateTaskOneCron")
public Result<Boolean> updateTaskOneCron(@RequestParam("cron") String cron){
taskOne.setCron(cron);
return Result.success(true);
}
我们用同样的方式增加TaskTwo...实现多定时任务。