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表达式
@Overridepublic String startTask() {ScheduledFuture<?> schedule = myThreadPoolTaskScheduler.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1s执行一次");}}, new CronTrigger("0/1 * * * * ?"));
定时执行一次
myThreadPoolTaskScheduler.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定时执行3s后开始执行");}},new Date(System.currentTimeMillis() + 3000));
在固定时间以固定速率执行
myThreadPoolTaskScheduler.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("定时执行3s后开始执行,固定3s执行一次");}},new Date(System.currentTimeMillis() + 3000),3000);
任务取消
private ScheduledFuture<?> schedule ;@Overridepublic String startTask() {schedule = myThreadPoolTaskScheduler.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("定时执行3s后开始执行,固定3s执行一次");}}, new Date(System.currentTimeMillis() + 3000), 3000);return "开启成功";}@Overridepublic 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;@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;@Overridepublic 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("调用了当前定时任务"));}
}