SpringBoot定时任务动态修改cron表达式改变执行周期

一、场景引入

前不久做过一个根据下载指令定时下载文件到服务器的需求。轮询下载的周期需要根据下载任务量的大小动态修改,下载任务密集的时候就周期缩小,下载任务少量时就扩大周期时间。java本身和第三方开源框架Spring共有三种执行定时任务的方式:

  1. Java自带的java.util.Timer类:这个类允许你调度一个java.util.TimerTask任务。(这种方式比较古老,自从第三方开源框架出现以来,基本不使用java自带的定时任务组件) 
  2. 开源的第三方框架: Quartz 或者 elastic-job , 但是这个比较复杂和重量级,适用于分布式场景下的定时任务,可以根据需要多实例部署定时任务。
  3. 使用Spring提供的注解: @Schedule 。 如果定时任务执行时间较短,并且比较单一,可以使用这个注解。

以上三种执行定时任务的方式都只能固定执行周期,一旦定时任务跑起来之后就不可能修改周期,只能修改周期后重新启动,现在需要动态修改执行周期,故这三种方式都不能使用。

二、解决方式

既然不能用通过传统的方式,那就要想到强大的第三方开源框架带来的便利,Spring从3.0版本开始在框架中加入了一个新的定时任务线程池配置类,即:  org.springframework.scheduling.concurrent包中有一个

1ThreadPoolTaskScheduler类,它继承了抽象类 ExecutorConfigurationSupport 并实现了 AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler三个接口 ,其中实现了 2 3TaskScheduler中的一个方法:ScheduledFuture<?> schedule(Runnable task, Trigger trigger)源码: 4 5@Overridepublic ScheduledFuture<?> schedule(Runnable task, Trigger trigger) { ScheduledExecutorService executor = getScheduledExecutor(); try { ErrorHandler errorHandler = (this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true)); return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule(); } catch (RejectedExecutionException ex) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); }}通过源码即可发现只要将对应的定时任务的线程以及包含cron表达式的 Trigger 参数传入即可按指定的周期启动定时任务。通过源码也可以发现它的线程池大小默认是16 7// ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(boolean) only available on JDK 7+private static final boolean setRemoveOnCancelPolicyAvailable = ClassUtils.hasMethod(ScheduledThreadPoolExecutor.class, "setRemoveOnCancelPolicy", boolean.class);private volatile int poolSize = 1;private volatile boolean removeOnCancelPolicy = false;private volatile ErrorHandler errorHandler;private volatile ScheduledExecutorService scheduledExecutor; 也就是说定时任务默认是单线程串行执行,如果同时需要执行多个定时任务的话,需要对线程池的大小进行配置,我配置的是线程大小是20,如下: 8 9@Bean(autowire = Autowire.BY_NAME, value = "threadPoolConfigBean")public ThreadPoolTaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(20); return scheduler;}然后编写定时任务接口并实现: 10 11public interface Task { /** * 开始任务 */ String startTask(); /** * 停止任务 */ void stopCron(); /** * 重新触发任务 * * @param cron * @return */ boolean triggerAgain(String cron);} 12 13实现类: 14 15public class CacheInfoDownloadTask implements Task { Environment env = SpringUtils.getBean(Environment.class); // spring上下文取bean private ThreadPoolTaskScheduler threadPoolTaskScheduler = (ThreadPoolTaskScheduler) SpringUtils.getBean("threadPoolConfigBean"); private CacheInfoDownloadBll cacheInfoDownloadBll = SpringUtils.getBean(CacheInfoDownloadBll.class); private ScheduledFuture<?> future; private static CacheInfoDownloadTask instance; // 通过单例向外提供唯一实例 public static synchronized CacheInfoDownloadTask getInstance() { if (null == instance) { instance = new CacheInfoDownloadTask(); } return instance; } @Override public String startTask() { String taskCorn = env.getProperty("cacheInfoDownload.schedule"); future = threadPoolTaskScheduler.schedule(new CacheInfoDownloadJob(), new CronTrigger(taskCorn)); log.info("缓存信息拉取开始...执行周期:{}", taskCorn); return "ok"; } @Override public void stopCron() { if (future != null) { future.cancel(true); } log.info("缓存信息拉取任务停止成功..."); } @Override public boolean triggerAgain(String cron) { this.stopCron(); future = this.threadPoolTaskScheduler.schedule(new CacheInfoDownloadJob(), new CronTrigger(cron)); log.info("缓存信息拉取任务更改cron表达式后再次触发成功...执行周期:{}", cron); return true; } private class CacheInfoDownloadJob implements Runnable { @Override public void run() { String schoolId = env.getProperty("school.id"); boolean result = cacheInfoDownloadBll.getCacheInfo(schoolId); if (result) log.info("缓存信息拉取并下载任务执行成功..."); else log.error("缓存信息拉取后存储失败..."); } }}定时任务编写成功后,使用静态工厂模式在Spring任务启动同时启动定时任务,在修改定时任务的执行周期时,只需把调用triggerAgain(String cron)方法即可修改: 16 17@Component@Order(value = 1)public class TaskTriggerOrderConfig implements CommandLineRunner { @Override public void run(String... strings) throws Exception { TaskFactory.getInstance(DOWNLOAD_TASK).startTask(); }}
点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

@Scheduled注解

1概述@Scheduled注解是springboot提供的用于定时任务控制的注解,主要用于控制任务在某个指定时间执行,或者每隔一段时间执行.注意需要配合@EnableScheduling使用,配置@Scheduled主要有三种配置执行时间的方式,cron,fixedRate,fixedDelay.2croncron是

Springboot自带定时任务实现动态配置Cron参数

同学们,我今天分享一下SpringBoot动态配置Cron参数。场景是这样子的:后台管理界面对定时任务进行管理,可动态修改执行时间,然后保存入库,每次任务执行前从库里查询时间,以达到动态修改Cron参数的效果。好,咱们一起来看看是怎么回事。1.Timer:这是java自带的java.util.Timer类,这个类允许你调度一个j

Linux的定时任务

任务计划的条件:1.在未来的某个时间点执行一次某个任务(atbatch)2.周期性的执行某个任务(cron)at在指定时间执行任务_用法_at\选项参数\\时间\_选项参数_\l      查看作业\c      显示即将执行任务的细节\d      使用任务id号

Java日期时间API系列29

  Java开发过程中经常会用到定时任务job的场景,比如定时处理数据报表等问题,开源作业调度框架也非常多,常用的开源作业调度框架有:SpringTask、Quartz和xxljob等。各个框架的具体使用不再这里讨论,这里主要讨论一下其中cron表达式的计算应用,xktime中的应用。1.SpringTask中cron表达式的计算应用

SpringBoot定时任务动态修改cron表达式改变执行周期

一、场景引入前不久做过一个根据下载指令定时下载文件到服务器的需求。轮询下载的周期需要根据下载任务量的大小动态修改,下载任务密集的时候就周期缩小,下载任务少量时就扩大周期时间。java本身和第三方开源框架Spring共有三种执行定时任务的方式:1)Java自带的java.util.Timer类:这个类允许你调度一个java.util.TimerT