ThreadPoolTaskScheduler Spring 中动态添加定时任务
大约 3 分钟
Spring 中定时任务 ThreadPoolTaskScheduler
开发中经常会遇到需要定时任务的需求,这里分享一下我写的工具类。
先上代码
SchedulerService
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
/**
* 定时任务调度服务
* @author sa@linkot.cn
*/
@Service
@Slf4j
public class SchedulerService {
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
private final Map<String, ScheduledFuture<?>> map = new HashMap<>();
public SchedulerService(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
}
/**
* 当前是否存在该key的任务
* @param key 任务key
*/
public boolean exist(String key){
return map.get(key)!=null;
}
/**
* 不进行管理的一次性任务,任务必定会执行且无法取消
*/
public void schedule(Runnable runnable, Date startAt) {
threadPoolTaskScheduler.schedule(runnable, startAt);
}
/**
* 进行管理的一次性任务,任务一段时间后执行,执行前可取消
*/
public void schedule(String key, Runnable runnable, Date startAt) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.schedule(new AutoRemoveTask(key, runnable, this), startAt));
}
/**
* 进行管理的一次性任务,任务一段时间后执行,执行前可取消,执行后不自动移除
*/
public void scheduleDontRemove(String key, Runnable runnable, Date startAt) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.schedule(runnable, startAt));
}
/**
* 进行管理的任务,使用触发器判断是否执行,可取消
*/
public void schedule(String key, Runnable runnable, Trigger trigger) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.schedule(runnable, trigger));
}
/**
* 进行管理的任务,以固定间隔重复执行,可取消
*/
public void scheduleAtFixedRate(String key, Runnable runnable, Long period) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.scheduleAtFixedRate(runnable, period));
}
/**
* 进行管理的任务,指定日期后开始按固定间隔执行,可取消
*/
public void scheduleAtFixedRate(String key, Runnable runnable, Date startAt, Long period) {
if (exist(key)) cancel(key, "取消旧任务");
map.put(key, threadPoolTaskScheduler.scheduleAtFixedRate(runnable, startAt, period));
}
/**
* 取消某个key的定时任务
*/
public void cancel(String key){
cancel(key, true, "");
}
/**
* 取消某个key的定时任务
*/
public void cancel(String key, String reason){
cancel(key, true, reason);
}
/**
* 取消某个key的定时任务
* @param interrupt 是否强制取消
*/
public void cancel(String key, boolean interrupt, String reason){
if (ObjectUtils.isEmpty(key)) {
return;
}
log.info("取消定时任务:{} -> {}", key, reason);
ScheduledFuture<?> s = map.get(key);
if (s!=null){
s.cancel(interrupt);
map.remove(key);
}else {
log.info("任务不在列表中:{},不存在或已经执行完毕", key);
}
}
/**
* 执行完一次后删除自身的任务
* @author sa@linkot.cn
*/
private static class AutoRemoveTask implements Runnable{
private final String key;
private final Runnable runnable;
private final SchedulerService service;
/**
* 执行一次后自动删除的任务
* @param key 任务 key
* @param runnable 执行
* @param service 管理定时任务的service
*/
public AutoRemoveTask(String key, Runnable runnable,
SchedulerService service) {
this.key = key;
this.runnable = runnable;
this.service = service;
}
@Override
public void run() {
runnable.run();
service.cancel(key);
}
}
}
使用例:定时一次性任务
场景:2小时后删除一条记录
class Test{
@Autowire private ScheduleService scheduleService;
public void test(){
//伪代码:插入一条 id 为 10 的记录
schedulerService.schedule(()->{
//伪代码:删除 id 为 10 的记录
}, new Date(System.currentTimeMillis() * 1000 * 60 * 60 * 2));
}
}
使用例:定时一次性任务,可取消
场景:用户头像上传,若没有使用到改该头像,10分钟后自动从文件服务器删除
class Test{
@Autowire private ScheduleService scheduleService;
public String upload(){
String url = //头像上传后返回访问url、以 url 为定时任务 key,取消时通过该 url 取消
schedulerService.schedule(url, ()->{
//伪代码:从文件服务器删除这个图片
}, new Date(System.currentTimeMillis() * 1000 * 60 * 10));
return url;
}
public void saveUser(User user){
//保存用户到数据库,取消自动删除头像文件的任务
//用户头像 url 为之前的定时任务 key
scheduleService.cancel(user.getAvatarUrl());
//伪代码:保存用户数据
}
}
使用例:cron触发器定时任务
场景:每月1号0点清空用户积分
class Test{
@Autowire private ScheduleService scheduleService;
public void test(String id){
schedulerService.schedule(id, ()->{
//伪代码:更新所有用户积分为0
}, new CronTrigger("0 0 0 1 * ? *"));
}
}