Springboot使用ThreadPoolTaskScheduler轻量级多线程定时任务框架
简介: Spring注解定时任务使用不是很灵活,如果想要灵活的配置定时任务,可以使用xxl-job 或者 quartz等定时任务框架,但是过于繁琐,可能成本较大。所以可以使用ThreadPoolTaskScheduler来灵活处理定时任务
ThreadPoolTaskScheduler是什么
ThreadPoolTaskScheduler
是 Spring Framework 中的一部分,主要用于调度任务。它基于线程池,可以处理异步任务和定时任务
主要api
- schedule(Runnable task, Trigger trigger) corn表达式,周期执行
- schedule(Runnable task, Date startTime) 定时执行
- scheduleAtFixedRate(Runnable task, Date startTime, long period)
定时周期间隔时间执行。间隔时间单位 TimeUnit.MILLISECONDS - scheduleAtFixedRate(Runnable task, long period) 间隔时间以固定速率执行。单位毫秒
固定速率执行不会管上次执行的状态如何
在使用前需要配置下ThreadPoolTaskScheduler
@Configuration
public class SchedulingTaskConfig {
@Bean(name = "myThreadPoolTaskScheduler")
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(60);
taskScheduler.setThreadNamePrefix("task-");
taskScheduler.setAwaitTerminationSeconds(3000);
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return taskScheduler;
}
}
cron表达式
@Override
public String startTask() {
ScheduledFuture<?> schedule = myThreadPoolTaskScheduler.schedule(new Runnable() {
@Override
public void run() {
System.out.println("1s执行一次");
}
}, new CronTrigger("0/1 * * * * ?"));
定时执行一次
myThreadPoolTaskScheduler.schedule(new Runnable() {
@Override
public void run() {
System.out.println("定时执行3s后开始执行");
}
},new Date(System.currentTimeMillis() + 3000));
在固定时间以固定速率执行
myThreadPoolTaskScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("定时执行3s后开始执行,固定3s执行一次");
}
},new Date(System.currentTimeMillis() + 3000),3000);
任务取消
private ScheduledFuture<?> schedule ;
@Override
public String startTask() {
schedule = myThreadPoolTaskScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("定时执行3s后开始执行,固定3s执行一次");
}
}, new Date(System.currentTimeMillis() + 3000), 3000);
return "开启成功";
}
@Override
public String stopTask() {
if (schedule != null){
schedule.cancel(true);
System.out.println("任务取消成功");
return "取消成功";
}
return "取消失败";
}
实现页面控制定时任务开关
将定时任务保存到数据库中,并在页面上实现定时任务的开关,以及更新定时任务时间后重新创建定时任务
数据库实体
@TableName("task")
@Data
public class ScheduleTask {
public interface Update{};
@TableId(type = IdType.AUTO)
@NotNull(message = "任务id不能为空",groups = Update.class)
private Integer id;
@NotBlank(message = "请填写任务执行类")
@TableField("task_clazz")
private String taskClazz;
@NotBlank(message = "请填写任务执行方法")
@TableField("task_method")
private String taskMethod;
@NotBlank(message = "请填写任务执行时间,采用cron格式")
@TableField("cron")
private String cron;
@TableLogic
@TableField("status")
private Integer status;
}
contrloller
@RestController
@RequiredArgsConstructor
public class TaskManagerController {
private final TaskManagerService taskManagerService;
@PostMapping("/addTask")
public Boolean addTask(@RequestBody @Validated ScheduleTask task){
return taskManagerService.addTask(task);
}
@PostMapping("/stopTask/{id}")
public Boolean stopTask(@PathVariable Integer id){
return taskManagerService.stopTask(id);
}
}
service
@Service
@Slf4j
@RequiredArgsConstructor
public class TaskManagerServiceImpl implements TaskManagerService {
private final ScheduleTaskMapper scheduleTaskMapper;
private final TaskManager taskManager;
@Override
public Boolean addTask(ScheduleTask task) {
int i = scheduleTaskMapper.insert(task);
if (i > 0){
TaskRunnable taskRunnable = new TaskRunnable(task.getTaskClazz(), task.getTaskMethod());
taskManager.addTask(String.valueOf(task.getId()),taskRunnable,task.getCron());
return true;
}
return false;
}
@Override
public Boolean stopTask(Integer id) {
int i = scheduleTaskMapper.deleteById(id);
if (i> 0){
taskManager.stopTask(String.valueOf(id));
return true;
}
return false;
}
}
TaskRunnable
通过此类获取具体的执行方法
@Slf4j
public class TaskRunnable implements Runnable{
/**
* 定时任务类
*/
private final String clazz;
/**
* 定时任务方法
*/
private final String methodName;
public TaskRunnable(String clazz, String methodName) {
this.clazz = clazz;
this.methodName = methodName;
}
@Override
public void run() {
try {
//获取类
Object bean = SpringContextUtils.getBean(clazz);
//获取方法
Method method = bean.getClass().getDeclaredMethod(methodName);
//设置方法可用
ReflectionUtils.makeAccessible(method);
//执行方法
method.invoke(bean);
} catch (Exception e) {
log.error("获取方法信息报错:{}",e.getMessage());
throw new RuntimeException(e);
}
}
}
任务调度类
@Component
@RequiredArgsConstructor
@Slf4j
public class TaskManager {
private final ThreadPoolTaskScheduler myThreadPoolTaskScheduler;
public static ConcurrentHashMap<String, ScheduledFuture<?>> cache = new ConcurrentHashMap<>();
/**
* 创建定时任务
* @param key 任务key
* @param taskRunnable 当前线程
* @param cron 定时任务cron
*/
public void addTask(String key ,TaskRunnable taskRunnable ,String cron){
//取消任务
this.stopTask(key);
ScheduledFuture<?> schedule = myThreadPoolTaskScheduler.schedule(taskRunnable, new CronTrigger(cron));
if (schedule != null){
cache.put(key,schedule);
log.info("当key为{}的定时任务创建成功",key);
}
}
public void stopTask(String key){
if (cache.get(key) == null){
log.info("当前没有key为{}的定时任务",key);
return;
}
ScheduledFuture<?> scheduledFuture = cache.get(key);
if (scheduledFuture != null){
scheduledFuture.cancel(true);
cache.remove(key);
log.info("当前key为{}的定时任务已取消",key);
}
}
}
工具类
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtils.context = applicationContext;
}
public static Object getBean(String name){
return context.getBean(name);
}
}
方法测试
@Slf4j
@Component(value = "testTask")
public class TestTask {
public void taskMethod(){
log.info(String.format("调用了当前定时任务"));
}
}