Dynamic Adjustment of Scheduled Tasks in SpringBoot Using ThreadPoolTaskScheduler
This article explains how to dynamically modify the execution time of SpringBoot scheduled tasks by combining ScheduledTaskRegistrar with ThreadPoolTaskScheduler, decoupling business logic from triggers, and provides complete code examples for adding, updating, and removing tasks at runtime.
In SpringBoot projects, clients may require a business to be executed at a specific time and later adjust that execution time dynamically.
Using only ScheduledTaskRegistrar cannot achieve dynamic adjustments because changes require service restart, tasks cannot be deleted, and business logic is tightly coupled with the trigger.
The article proposes using ThreadPoolTaskScheduler together with its schedule(Runnable, Trigger) method to decouple business logic from the trigger and allow dynamic cron expressions.
Implementation steps: instantiate ThreadPoolTaskScheduler , store ScheduledFuture in a map keyed by task ID, and add or remove tasks via static utility methods.
/**
* @author Created by zhuzqc on 2023/1/30 15:28
*/
@Slf4j
@Component
@EnableScheduling
public class ScheduleTaskDemo implements SchedulingConfigurer {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// Runnable thread registers task
Runnable taskOne = () -> {
// business logic
logger.info("----------业务执行结束----------");
};
// Trigger time, usually a cron expression
Trigger triggerOne = triggerContext -> {
Date nextExecTime = null;
try {
// specify cron expression here
String cron = "0 00 12 ? * *";
if (StringUtils.isBlank(cron)) {
logger.info("trigger定时器的 cron 参数为空!");
cron = "0 00 12 ? * *"; // default to noon
}
logger.info("---------->定时任务执行中<---------");
CronTrigger cronTrigger = new CronTrigger(cron);
nextExecTime = cronTrigger.nextExecutionTime(triggerContext);
} catch (Exception e) {
e.printStackTrace();
log.info(e.getMessage());
}
return nextExecTime;
};
taskRegistrar.addTriggerTask(taskOne, triggerOne);
}
}Utility class TaskSchedulerUtil provides put(runnable, trigger, id) to add tasks and delete(id) to cancel them, while maintaining a map of task IDs to their ScheduledFuture objects.
/**
* @author Created by zhuzqc on 2023/1/30 15:39
* 任务线程池管理工具
*/
public class TaskSchedulerUtil {
private static final Logger logger = LoggerFactory.getLogger(TaskSchedulerUtil.class);
/** 线程调度工具对象,作为该类的成员变量 */
private static ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
/** 初始化 map 对象,装配 schedule 方法的返回对象为 value 值 */
private static Map
> scheduledFutureMap = new HashMap<>();
static {
threadPoolTaskScheduler.initialize();
}
/**
* 将Runnable对象和Trigger对象作为参数传入该静态方法
* @param runnable
* @param trigger
* @param id 定时任务id
*/
public static void put(Runnable runnable, Trigger trigger, String id) {
// 将携带有Runnable和Trigger的ScheduledFuture类对象作为 Map 的 value 进行装配
ScheduledFuture
scheduledFuture = threadPoolTaskScheduler.schedule(runnable, trigger);
// 放入 Map 中作为 value
scheduledFutureMap.put(id, scheduledFuture);
logger.info("---添加定时任务--- >" + id);
}
/**
* 通过上述 put 方法的参数id(定时任务id)标识,将定时任务移除出 map
* @param id
*/
public static void delete(String id) {
ScheduledFuture
scheduledFuture = scheduledFutureMap.get(id);
// 条件判断
if (scheduledFuture != null && !scheduledFuture.isCancelled()) {
scheduledFuture.cancel(true);
}
scheduledFutureMap.remove(id);
logger.info("---取消定时任务--- >" + id);
}
}A demo class TaskScheduleDemo shows how to inject external business data (mapper, service) and schedule tasks with a dynamic cron expression derived from an entity's send time, using the utility methods above.
/**
* @author Created by zhuzqc on 2023/1/30 15:58
*/
@Slf4j
@Component
@EnableScheduling
public class TaskScheduleDemo {
@Resource
private AAAMapper aaaMapper;
@Resource
private BBBService bbbService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
// 引入外部的业务数据对象
public void putHiredTask(CCCEntity cccEntity) {
// 将业务线程和定时触发器交由线程池工具管理:创建业务线程对象,并对属性赋初始化值(有参构造)
TaskSchedulerUtil.put(
new TaskThreadDemo(cccEntity, aaaMapper, bbbService),
new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// 获取定时触发器,这里可以获取页面的更新记录,实现定时间隔的动态调整
Date nextExecTime = TaskTransUtils.StringToDateTime(cccEntity.getSendTime());
// cron 表达式转换工具类
String cron = TaskTransUtils.getDateCronTime(nextExecTime);
try {
if (StringUtils.isBlank(cron)) {
logger.info("trackScheduler定时器的 cron 参数为空!");
// 默认值,每天早上9:00
cron = "0 00 09 ? * *";
}
logger.info("-------定时任务执行中:" + cron + "--------");
CronTrigger cronTrigger = new CronTrigger(cron);
nextExecTime = cronTrigger.nextExecutionTime(triggerContext);
} catch (Exception e) {
e.printStackTrace();
log.info(e.getMessage());
}
return nextExecTime;
}
},
"该定时任务的id"
);
}
}Conclusion: using only ScheduledTaskRegistrar is insufficient for dynamic task timing; combining ThreadPoolTaskScheduler with its schedule() method enables runtime adjustments, and separating business logic from the trigger allows both parts to access external data.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.