SpringBoot 定时任务进阶——动态 cron、分布式定时任务

SpringBoot 定时任务进阶——动态 cron、分布式定时任务 基础的 Scheduled 注解只能写死 cron 表达式修改需要重启。这篇讲动态定时任务和分布式定时任务。一、ScheduledTaskRegistrar 动态注册ComponentpublicclassDynamicScheduledTask{AutowiredprivateScheduledTaskRegistrartaskRegistrar;publicvoidaddTask(Stringcron,Runnabletask){taskRegistrar.addCronTask(task,cron);}publicvoidremoveTask(Runnabletask){taskRegistrar.removeScheduledTasks();}}二、动态修改 cronComponentpublicclassDynamicCronService{AutowiredprivateScheduledTaskRegistrartaskRegistrar;privateScheduledFuture?future;publicvoidupdateCron(StringnewCron){if(future!null){future.cancel(false);}futuretaskRegistrar.scheduleCronTask(()-{System.out.println(执行定时任务: LocalDateTime.now());},newCron);}}三、配置中心动态 cronComponentRefreshScopepublicclassConfigCenterCron{privatefinalAtomicIntegercounternewAtomicInteger(0);Scheduled(cron${task.report.cron:0 0 2 * * ?})publicvoidreportTask(){log.info(报表任务执行: {},counter.incrementAndGet());}}通过 Nacos 修改配置后定时任务的 cron 表达式自动刷新不需要重启。四、分布式定时任务ComponentpublicclassDistributedScheduledTask{AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalStringLOCK_KEYlock:scheduled:report;/** * 每 10 秒执行一次但只有拿到分布式锁的实例才执行 */Scheduled(fixedDelay10000)publicvoidreportTask(){StringlockValueUUID.randomUUID().toString();try{// 尝试获取锁过期时间 8 秒BooleanlockedredisTemplate.opsForValue().setIfAbsent(LOCK_KEY,lockValue,8,TimeUnit.SECONDS);if(Boolean.TRUE.equals(locked)){log.info(获取到锁执行报表任务);// 执行任务...}}finally{// 只释放自己的锁StringvalueredisTemplate.opsForValue().get(LOCK_KEY);if(lockValue.equals(value)){redisTemplate.delete(LOCK_KEY);}}}}五、task.name 做唯一标识ComponentpublicclassInstanceTask{privatestaticfinalLoggerlogLoggerFactory.getLogger(InstanceTask.class);Value(${spring.application.name})privateStringappName;privatefinalStringinstanceIdIdUtil.simpleUUID().substring(0,8);/** * 每台机器各自执行自己的任务 * 适合每台机器独立处理的任务如清理本地日志 */Scheduled(cron0 0 3 * * ?)publicvoidcleanLogTask(){log.info([{}] {} 清理日志,appName,instanceId);}}六、手动启动停止ComponentpublicclassTaskManager{privatefinalMapString,ScheduledFuture?taskMapnewConcurrentHashMap();AutowiredprivateTaskSchedulertaskScheduler;publicvoidstartTask(StringtaskName,Runnabletask,Stringcron){if(taskMap.containsKey(taskName)){log.warn(任务已存在: {},taskName);return;}CronTriggertriggernewCronTrigger(cron);ScheduledFuture?futuretaskScheduler.schedule(task,trigger);taskMap.put(taskName,future);log.info(任务已启动: {},taskName);}publicvoidstopTask(StringtaskName){ScheduledFuture?futuretaskMap.get(taskName);if(future!null){future.cancel(false);taskMap.remove(taskName);log.info(任务已停止: {},taskName);}}publicvoidupdateTaskCron(StringtaskName,Runnabletask,StringnewCron){stopTask(taskName);startTask(taskName,task,newCron);log.info(任务已更新: {} → {},taskName,newCron);}}七、常见问题// 1. Scheduled 默认单线程运行// 如果一个任务卡住了其他所有任务都会卡住// 解决配置线程池ConfigurationpublicclassSchedulingConfig{BeanpublicTaskSchedulertaskScheduler(){ThreadPoolTaskSchedulerschedulernewThreadPoolTaskScheduler();scheduler.setPoolSize(5);scheduler.setThreadNamePrefix(scheduled-);returnscheduler;}}// 2. cron 表达式在线生成// 推荐工具百度搜索 cron 在线 可视化生成总结简单固定任务 → Scheduled(cron ...) 动态修改 cron → TaskScheduler.schedule() 分布式互斥 → Redis 分布式锁 配置中心动态 → RefreshScope 觉得有用的话点赞 关注【张老师技术栈】吧每周更新 Java/Python/MySQL 实战干货不让你白来。