Backend Development 9 min read

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.

Java Captain
Java Captain
Java Captain
Dynamic Adjustment of Scheduled Tasks in SpringBoot Using ThreadPoolTaskScheduler

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.

Javabackend developmentSpringBootDynamic SchedulingScheduledTaskThreadPoolTaskScheduler
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.