基于注解(@Scheduled)
@Configuration
@EnableScheduling
public class MyScheduleConfig {
@Scheduled(cron = "0/5 * * * * ?")
private void myTasks() {
System.out.println("执行定时任务 " + LocalDateTime.now());
}
}
查看cron表达式
- 启动应用,控制台看效果

- 简单方便
- 缺点:修改定时任务的执行周期或者停止时,需要修改代码,重启。
数据库动态配置
CREATE TABLE `scheduled_job` (
`job_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`job_key` varchar(128) NOT NULL COMMENT '定时任务完整类名',
`cron_expression` varchar(20) NOT NULL COMMENT 'cron表达式',
`task_explain` varchar(50) NOT NULL DEFAULT '' COMMENT '任务描述',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1:正常;-1:停用',
PRIMARY KEY (`job_id`),
UNIQUE KEY `job_key` (`job_key`),
UNIQUE KEY `cron_key_unique_idx` (`job_key`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='定时任务表';
- 插入两条数据,job_key是完整的类名
 - 创建定时任务线程池
@Configuration
@Slf4j
public class ScheduledConfig {
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
log.info("创建定时任务调度线程池 start");
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(20);
threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
log.info("创建定时任务调度线程池 end");
return threadPoolTaskScheduler;
}
}
@Slf4j
@Component
public class ScheduledTaskRunner implements ApplicationRunner {
@Autowired
private ScheduledTaskService scheduledTaskService;
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("----初始化定时任务开始----");
scheduledTaskService.initTask();
log.info("----初始化定时任务完成----");
}
}
public interface ScheduledOfTask extends Runnable{
void execute();
@Override
default void run() {
execute();
}
}
@Component
@Slf4j
public class TaskJob1 implements ScheduledOfTask{
@Override
public void execute() {
log.info("执行任务1 "+ LocalDateTime.now());
}
}
@Component
@Slf4j
public class TaskJob2 implements ScheduledOfTask{
@Override
public void execute() {
log.info("执行任务2 "+ LocalDateTime.now());
}
}
public interface ScheduledTaskService{
Boolean start(ScheduledJob scheduledJob);
Boolean stop(String jobKey);
Boolean restart(ScheduledJob scheduledJob);
void initTask();
}
@Slf4j
@Service
public class ScheduledTaskServiceImpl implements ScheduledTaskService {
private ReentrantLock lock = new ReentrantLock();
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
public Map scheduledFutureMap = new ConcurrentHashMap<>();
@Autowired
private ScheduledJobService scheduledJobService;
@Override
public Boolean start(ScheduledJob scheduledJob) {
String jobKey = scheduledJob.getJobKey();
log.info("启动定时任务"+jobKey);
lock.lock();
log.info("加锁完成");
try {
if(this.isStart(jobKey)){
log.info("当前任务在启动状态中");
return false;
}
this.doStartTask(scheduledJob);
} finally {
lock.unlock();
log.info("解锁完毕");
}
return true;
}
private Boolean isStart(String taskKey) {
if (scheduledFutureMap.containsKey(taskKey)) {
if (!scheduledFutureMap.get(taskKey).isCancelled()) {
return true;
}
}
return false;
}
@Override
public Boolean stop(String jobKey) {
log.info("停止任务 "+jobKey);
boolean flag = scheduledFutureMap.containsKey(jobKey);
log.info("当前实例是否存在 "+flag);
if(flag){
ScheduledFuture scheduledFuture = scheduledFutureMap.get(jobKey);
scheduledFuture.cancel(true);
scheduledFutureMap.remove(jobKey);
}
return flag;
}
@Override
public Boolean restart(ScheduledJob scheduledJob) {
log.info("重启定时任务"+scheduledJob.getJobKey());
this.stop(scheduledJob.getJobKey());
return this.start(scheduledJob);
}
public void doStartTask(ScheduledJob sj){
log.info(sj.getJobKey());
if(sj.getStatus().intValue() != 1)
return;
Class clazz;
ScheduledOfTask task;
try {
clazz = Class.forName(sj.getJobKey());
task = (ScheduledOfTask) SpringContextUtil.getBean(clazz);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("spring_scheduled_cron表数据" + sj.getJobKey() + "有误", e);
}
Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(task,(triggerContext -> new CronTrigger(sj.getCronExpression()).nextExecutionTime(triggerContext)));
scheduledFutureMap.put(sj.getJobKey(),scheduledFuture);
}
@Override
public void initTask() {
List list = scheduledJobService.list();
for (ScheduledJob sj : list) {
if(sj.getStatus().intValue() == -1)
continue;
doStartTask(sj);
}
}
}
- 获取Bean的工具类SpringContextUtil
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringContextUtil.applicationContext == null){
SpringContextUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
public static T getBean(Class clazz){
return getApplicationContext().getBean(clazz);
}
public static T getBean(String name,Class clazz){
return getApplicationContext().getBean(name, clazz);
}
}
@Data
@TableName("scheduled_job")
public class ScheduledJob {
@TableId(value = "job_id",type = IdType.AUTO)
private Integer jobId;
private String jobKey;
private String cronExpression;
private String taskExplain;
private Integer status;
}
public interface ScheduledJobMapper extends BaseMapper {
}
public interface ScheduledJobService extends IService {
boolean updateOne(ScheduledJob scheduledJob);
}
@Service
@Slf4j
public class ScheduledJobServiceImpl extends ServiceImpl implements ScheduledJobService{
@Autowired
private ScheduledTaskService scheduledTaskService;
@Override
public boolean updateOne(ScheduledJob scheduledJob) {
if(updateById(scheduledJob))
scheduledTaskService.restart(getById(scheduledJob.getJobId()));
return true;
}
}
@RestController
@RequestMapping("/job")
public class ScheduledJobController {
@Autowired
private ScheduledJobService scheduledJobService;
@PostMapping(value = "/update")
public CallBackResult update(HttpServletRequest request, ScheduledJob scheduledJob){
if(scheduledJobService.updateOne(scheduledJob))
return new CallBackResult(true,"修改成功");
return new CallBackResult(false,"修改失败");
}
}

可以看到任务1是每5秒执行一次,任务2是12秒执行一次
修改cron,执行周期改为20秒执行一次,状态不变   
再看控制台输出结果,任务2没变化,任务1由5秒一次变成了20秒一次了 
- 修改状态
  再看控制台输出结果,任务2没变化,任务1已经不再执行了

支持通过接口的方式去改动,并且不需要重启
|