静态:基于注解
(1)在 main 中(这里就是启动类)类声明开启定时任务的注解 @EnableScheduling:
package com.springboot20.xdclass.springboot.demo2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling //开启定时任务注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
(2)自定义一个定时任务业务类TestTask.java:
编写定时任务注解 @Scheduled
package com.springboot20.xdclass.springboot.demo2.task;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 功能描述:定时任务业务类
* @author caoxianshuo
*
*/
@Component
public class TestTask {
// 注意:定时方法返回值只能是void且不能有传入参数
@Scheduled(fixedRate=2000)
public void testTask(){
System.out.println("当前时间:"+new Date());
}
}
注意:
“ fixedDelay: 每次方法执行完毕后,等待5s再执行此方法,
同时只能有一个线程运行此方法 ”
@Scheduled(fixedDelay=5000)
“ fixedRate: 每隔5s调用一次此方法,无论之前的方法是否执行完毕,
同时可能有N个线程执行此方法 ”
@Scheduled(fixedRate=5000)
“ initialDelay: 第一次调用此方法前的等待时间 ”
@Scheduled(initialDelay=1000, fixedRate=5000)
“ cron:通过cron配置值 ”
/**
* 支持cron语法:
* 每个参数的意义分别是: second, minute, hour, day of month, month, day of week
*
* 如下:周一至周五,每隔5s执行一次方法
*/
@Scheduled(cron="*/5 * * * * SUN-MON")
(3)执行测试结果:
显然,使用@Scheduled 注解很方便,但缺点是当我们调整了执行周期的时候,需要重启应用才能生效,这多少有些不方便。为了达到实时生效的效果,可以使用接口来完成定时任务。
动态:基于接口
(1)整合持久层框架Mybatis,连接数据库
(2)添加数据库记录:
开启本地数据库mysql,随便打开查询窗口,然后执行脚本内容,如下:
DROP DATABASE IF EXISTS `socks`;
CREATE DATABASE `socks`;
USE `SOCKS`;
DROP TABLE IF EXISTS `t_cron`;
CREATE TABLE `t_cron` (
`cron_id` varchar(30) NOT NULL PRIMARY KEY,
`cron` varchar(30) NOT NULL
);
INSERT INTO `cron` VALUES ('1', '0/10 * * * * ?');
(3)创建定时器:
数据库准备好数据之后,我们编写定时任务,注意这里添加的是TriggerTask,目的是循环读取我们在数据库设置好的执行周期,以及执行相关定时任务的内容。
新建一个实现SchedulingConfigurer接口的实体类MyScheduleTask,具体代码如下:
package com.springboot20.xdclass.springboot.demo2.task;
import java.util.Date;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* 功能描述:基于接口形式实现动态定时任务
* @author caoxianshuo
*
*/
@Component
@Configuration // 主要用于标记配置类,兼具Component的效果
@EnableScheduling // 开启定时任务
public class MyScheduleTask implements SchedulingConfigurer {
// 定义一个内部Mapper接口类
@Mapper
public interface CronMapper {
@Select("Select cron from t_cron limit 1")
public String getCron();
}
// 注入Mapper
@Autowired
private CronMapper cronMapper;
/**
* 功能描述:执行定时任务
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
// 1.添加任务内容(Runnable)
() -> System.out.println("执行动态定时任务: " + new Date()),
// 2.设置执行周期(Trigger)
triggerContext -> {
// 2.1 从数据库获取执行周期
String cron = cronMapper.getCron();
// 2.2 合法性校验.
if (StringUtils.isEmpty(cron)) {
// Omitted Code ..
}
// 2.3 返回执行周期(Date)
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
}
}
控制台执行结果:
然后打开Navicat ,将执行周期修改为每6秒执行一次,如图:
查看控制台,发现执行周期已经改变,并且不需要我们重启应用,十分方便。如图:
注意: 如果在数据库修改时格式出现错误,则定时任务会停止,即使重新修改正确;此时只能重新启动项目才能恢复。
多线程定时任务:
基于注解设定多线程定时任务
package com.springboot20.xdclass.springboot.demo2.task;
import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@EnableScheduling //开启定时任务
@EnableAsync //开启多线程
public class MyThreadScheduleTask {
@Async
@Scheduled(fixedDelay = 1000) //间隔1秒
public void first() throws InterruptedException{
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
Thread.sleep(1000 * 10);
}
@Async
@Scheduled(fixedDelay = 2000) //间隔1秒
public void second(){
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
}
启动测试:
从控制台可以看出,第一个定时任务和第二个定时任务互不影响;
并且,由于开启了多线程,第一个任务的执行时间也不受其本身执行时间的限制,所以需要注意可能会出现重复操作导致数据异常。