最近遇到一个需求,定时任务的业务逻辑不会改变,但需要动态添加、移除定时任务,而且定时执行时间有可能随时改变,这可怎么实现呢?
首先,配置定时任务线程池;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 定时任务线程池配置类
* @author 程就人生
* @Date
*/
@Configuration
public class AsyncTheadConfig {
@Bean("threadPoolTaskScheduler")
public ThreadPoolTaskScheduler getThreadPoolTaskScheduler(){
// 定时任务线程池
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
// 线程池大小
executor.setPoolSize(10);
// 线程执行前缀
executor.setThreadNamePrefix("ThreadPoolTaskScheduler-");
// executor.setWaitForTasksToCompleteOnShutdown(true);
// executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
第二步,建立任务,里面包含了定时任务需要实现的业务逻辑;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* 定时任务的业务逻辑
* @author 程就人生
* @Date
*/
public class TimerThread implements Runnable{
private String uid;
public TimerThread(String uid){
this.uid = uid;
}
@Override
public void run() {
ZonedDateTime zdt = ZonedDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(Thread.currentThread().getName() + " 任务:" + uid + ",执行时间:" + zdt.format(dtf));
}
}
第三步,应用定时任务,包括添加、移除;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/index")
@RestController
public class IndexController1 {
// 定时任务线程池
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
// 任务队列管理
@SuppressWarnings("rawtypes")
private ConcurrentHashMap<String, ScheduledFuture> futureMap = new ConcurrentHashMap<String, ScheduledFuture>();
// 加入新的任务进来
@SuppressWarnings({ "rawtypes" })
@GetMapping("/insert/{uid}/{time}")
public Object insert(@PathVariable("uid") String uid, @PathVariable("time") String time){
// 定时任务执行类
TimerThread timerCollectData = new TimerThread(uid);
// 通过ThreadPoolTaskScheduler类,设定定时时间
ScheduledFuture future = threadPoolTaskScheduler.schedule(timerCollectData, new CronTrigger("*/"+time+" * * * * ?"));
// 加入到队列中
futureMap.put(uid, future);
return null;
}
// 移除已有的一个任务
@SuppressWarnings("rawtypes")
@GetMapping("/remove/{uid}")
public Object remove(@PathVariable("uid") String uid){
ScheduledFuture scheduledFuture = futureMap.get(uid);
if(scheduledFuture != null){
// 取消定时任务
scheduledFuture.cancel(true);
// 如果任务取消需要消耗点时间
boolean cancelled = scheduledFuture.isCancelled();
while (!cancelled) {
scheduledFuture.cancel(true);
System.out.println(uid + "取消中");
}
// 最后从队列中删除
futureMap.remove(uid);
}
return null;
}
}
最后,运行入口程序,打开浏览器进行测试;通过浏览器分别执行了localhost:8080/index/insert/1000/10、localhost:8080/index/insert/2000/20,也就是添加了两个任务,任务1000每10s执行一次,任务2000每20s执行一次;
执行 http://localhost:8080/index/remove/1000,把1000的任务移除掉,再看执行结果,只剩下任务2000,ok,动态添加、移除定时任务编码完成。
当然,这里为了测试,把管理任务的队列直接放到了Controller里,实际应用时应保持全局唯一。
最后总结
通过这个需求,我们又用到了一个类ThreadPoolTaskScheduler,它有别于ThreadPoolTaskExecutor类,有兴趣有时间的可以查看源码。
动态添加、移除定时任务的操作流程,大致可以分为以下四个步骤:
1.建立一个定时任务线程池;
2.为定时任务线程池建立一个队列,来管理这些任务;
3.根据唯一标识,往定时任务线程池和队列里分别添加这个任务;
4.根据唯一标识,从定时任务线程池里取消一个任务,并从队列里移除这个任务。