定时任务的创建
在 Spring 中 可以通过 @EnableScheduling 和 @Scheduled 这两个注解来实现定时任务,接下来看下这两个注解:
(1)@EnableScheduling
可以通过在启动类上添加@EnableScheduling开启定时任务功能
(2)@Scheduled
在定时任务方法上使用@Schedule注解标记定时任务并配置定时任务执行时机。
- 固定延迟时间:
// 初次调用延迟3秒,任务结束后5秒钟再次调用,两次调用不会同步运行
@Scheduled(initialDelay = 3000, fixedDelay = 5000)
public void timerTask() {
try {
System.out.println(System.currentTimeMillis() / 1000);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 固定间隔时间:
// 初次调用延迟3秒,每隔5秒钟调用一次,多次调用可能同步运行
@Scheduled(initialDelay = 3000, fixedRate = 5000)
public void timerTask() {
System.out.println(System.currentTimeMillis() / 1000);
}
- 通过cron表达式配置:
// 从第0秒开始每10秒钟执行一次
@Scheduled(cron = "0/10 * * * * ?")
public void timerTask() {
System.out.println(System.currentTimeMillis() / 1000);
}
// 从配置文件中读取cron表达式
@Scheduled(cron = "${task.cron}")
public void timerTask() {
System.out.println(System.currentTimeMillis() / 1000);
}
Cron表达式详解
动态修改定时任务配置
可以通过实现SchedulingConfigurer接口来配置定时任务,实现不用重启服务修改定时器的cron:
@Component
public class DynamicTask implements SchedulingConfigurer {
private CronTrigger trigger = new CronTrigger("5/10 * * * * ?");
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
System.out.println("来自定时器:" + System.currentTimeMillis() / 1000);
}
}, new Trigger() {
@Nullable
@Override
public Date nextExecutionTime(TriggerContext context) {
if (trigger != null) {
return trigger.nextExecutionTime(context);
}
return null;
}
});
}
public void setCron(String cron) {
try {
this.trigger = new CronTrigger(cron);
} catch (Exception e) {
e.printStackTrace();
}
}
}
但是上述方法在修改完cron表达式之后,只有再一次到达定时任务的时间,才会调用新的触发时间,导致页面设置的时间不能即时生效,以下改进方法可修正此问题:
@Service
public class ScheduleManager implements SchedulingConfigurer {
private final Log logger = LogFactory.getLog(getClass());
/**
* 受ScheduleManager管理的任务集合
*/
private final Map<Object, ScheduledTask> taskMap = new HashMap<>();
/**
* 定时任务注册器
*/
private ScheduledTaskRegistrar taskRegistrar;
/**
* 系统启动时获取任务注册对象
*
* @param taskRegistrar 任务注册对象
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
logger.info("scheduleManager has successfully acquired the taskRegistrar");
}
/**
* 添加新任务,如果存在key一致的任务,则取消原任务的执行,但添加新任务失败时原任务不会停止
*
* @param key 任务key
* @param runnable 新任务执行代码
* @param cron 新任务cron表达式
*/
public void addTask(Object key, Runnable runnable, String cron) {
if (runnable != null && !StringUtils.isEmpty(cron)) {
ScheduledTask oldTask = taskMap.get(key);
taskMap.put(key, taskRegistrar.scheduleCronTask(new CronTask(runnable, cron)));
if (oldTask != null) {
oldTask.cancel();
}
}
}
/**
* 重置任务的执行时机,修改失败时任务仍使用原有执行时机
*
* @param key 任务key
* @param cron 任务新cron表达式
*/
public void modifyTask(Object key, String cron) {
ScheduledTask oldTask = taskMap.get(key);
if (oldTask != null && !StringUtils.isEmpty(cron)) {
taskMap.put(key, taskRegistrar.scheduleCronTask(new CronTask(oldTask.getTask().getRunnable(), cron)));
oldTask.cancel();
}
}
/**
* 取消任务执行
*
* @param key 任务key
*/
public void cancelTask(Object key) {
ScheduledTask task = taskMap.remove(key);
if (task != null) {
task.cancel();
}
}
/**
* 查看是否存在任务
*
* @param key 任务key
* @return 如果任务存在返回true,否则返回false
*/
public boolean existTask(Object key) {
return taskMap.get(key) != null;
}
}
实现用户浏览记录的定时抽取
(1)配置@EnableScheduling
(2)实现定时抽取方法每20分钟抽取一次
@Component
public class ExtractTask {
@Resource
private ExtractData extractData;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
/**
* 定时抽取浏览数据 每20分钟一次
*/
@Scheduled(cron = "0/10 * * * * ?")
public void extract() {
System.out.println(System.currentTimeMillis() / 1000);
extractData.extractUserTagDate();
extractData.extractUserTagDate();
}
}