SpringBoot定時任務(wù)動態(tài)擴展ScheduledTaskRegistrar詳解
摘要
本文主要介紹基于SpringBoot
定時任務(wù)ScheduledTaskRegistrar
的動態(tài)擴展,實現(xiàn)定時任務(wù)的動態(tài)新增和刪除。
ScheduledTaskRegistrar類簡要描述
平常使用方式配置
Application
啟動類上添加注解@EnableScheduling
@EnableScheduling @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
- 在需要定時的方法上添加定時注解
@Scheduled(cron = "0/10 * * * * ?")
@Slf4j @Component public class OtherScheduler { @Scheduled(cron = "0/10 * * * * ?") public void print(){ log.info("每10S打印一次"); } @Scheduled(cron = "0/5 * * * * ?") public void print5(){ log.info("每5S打印一次"); } }
原理分析
默認(rèn)的方式啟動把ScheduledAnnotationBeanPostProcessor
該類實例化到SpringBoot
的Bean
管理中,并且該類持有一個ScheduledTaskRegistrar
屬性,然后掃描出來擁有@Scheduled
注解的方法,添加到定時任務(wù)中。
- 添加定時任務(wù)到列表中
掃描到@Scheduled
注解的時候調(diào)用了該方法添加任務(wù)
public void addCronTask(Runnable task, String expression) { if (!CRON_DISABLED.equals(expression)) { addCronTask(new CronTask(task, expression)); } }
- 啟動定時任務(wù)
在對象實例化完成后,調(diào)用了afterPropertiesSet
方法,該方法實際使用中執(zhí)行了
public void afterPropertiesSet() { scheduleTasks(); } protected void scheduleTasks() { if (this.taskScheduler == null) { this.localExecutor = Executors.newSingleThreadScheduledExecutor(); this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor); } if (this.triggerTasks != null) { for (TriggerTask task : this.triggerTasks) { addScheduledTask(scheduleTriggerTask(task)); } } if (this.cronTasks != null) { for (CronTask task : this.cronTasks) { addScheduledTask(scheduleCronTask(task)); } } if (this.fixedRateTasks != null) { for (IntervalTask task : this.fixedRateTasks) { addScheduledTask(scheduleFixedRateTask(task)); } } if (this.fixedDelayTasks != null) { for (IntervalTask task : this.fixedDelayTasks) { addScheduledTask(scheduleFixedDelayTask(task)); } } } private void addScheduledTask(@Nullable ScheduledTask task) { if (task != null) { this.scheduledTasks.add(task); } } // 啟動任務(wù)核心方法 public ScheduledTask scheduleCronTask(CronTask task) { ScheduledTask scheduledTask = this.unresolvedTasks.remove(task); boolean newTask = false; if (scheduledTask == null) { scheduledTask = new ScheduledTask(task); newTask = true; } if (this.taskScheduler != null) { scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger()); } else { addCronTask(task); this.unresolvedTasks.put(task, scheduledTask); } return (newTask ? scheduledTask : null); }
DynamicScheduledTaskRegistrar 動態(tài)任務(wù)注冊類
下面改動主要涉及到線程池數(shù)量、新增任務(wù)、刪除任務(wù)、銷毀任務(wù)四個方面;
public class DynamicScheduledTaskRegistrar extends ScheduledTaskRegistrar { private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskRegistrar.class); private final Map<String,ScheduledTask> scheduledTaskMap = new LinkedHashMap<>(16); public DynamicScheduledTaskRegistrar(){ super(); // 兩種實現(xiàn)方案 //ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); //TaskScheduler taskScheduler = new ConcurrentTaskScheduler(scheduledExecutorService); // 第二種實現(xiàn)方案 ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(8); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("dynamic-scheduled-task-"); taskScheduler.initialize(); this.setScheduler(taskScheduler); } /** * 新增任務(wù) * @param taskName * @param cron * @param runnable */ public Boolean addCronTask(String taskName,String cron,Runnable runnable){ if(scheduledTaskMap.containsKey(taskName)){ log.error("定時任務(wù)["+ taskName+"]已存在,添加失敗"); return Boolean.FALSE; } CronTask cronTask = new CronTask(runnable,cron); ScheduledTask scheduledTask = this.scheduleCronTask(cronTask); scheduledTaskMap.put(taskName,scheduledTask); log.info("定時任務(wù)["+taskName+"]新增成功"); return Boolean.TRUE; } /** * 刪除任務(wù) * @param taskName */ public void cancelCronTask(String taskName){ ScheduledTask scheduledTask = scheduledTaskMap.get(taskName); if(null != scheduledTask){ scheduledTask.cancel(); scheduledTaskMap.remove(taskName); } log.info("定時任務(wù)["+taskName+"]刪除成功"); } @Override public void destroy() { super.destroy(); scheduledTaskMap.values().forEach(ScheduledTask::cancel); } }
線程池數(shù)量問題
由于默認(rèn)是單線程的,如果任務(wù)阻塞時間過長則會導(dǎo)致后續(xù)的任務(wù)阻塞,所以盡量是異步任務(wù)或者是線程池數(shù)量大一點,則可以避免這個問題
DynamicScheduledTaskService
@Service public class DynamicScheduledTaskService { private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskService.class); private final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar = new DynamicScheduledTaskRegistrar(); /** * 新增任務(wù) * @param taskName * @param cron */ public void add(String taskName,String cron){ Boolean result = dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> print(taskName)); log.info("定時任務(wù)添加結(jié)果:" + result); } /** * 取消任務(wù) * @param taskName */ public void cancel(String taskName){ dynamicScheduledTaskRegistrar.cancelCronTask(taskName); } private void print(String taskName){ log.info(taskName+"開始"); try{ Thread.sleep(9000L); log.info(taskName+"結(jié)束111"); }catch (Exception ex){ } log.info(taskName+"結(jié)束"); } }
SchedulerController
@RestController @RequestMapping(value = "scheduler") public class SchedulerController { @Autowired private DynamicScheduledTaskService dynamicScheduledTaskService; @GetMapping(value = "add") public Object add(String taskName,String cron){ dynamicScheduledTaskService.add(taskName,cron); return "SUCCESS"; } @GetMapping(value = "cancel") public Object cancel(String jobName){ dynamicScheduledTaskService.cancel(jobName); return "SUCCESS"; } }
測試結(jié)果
新增的任務(wù)都睡眠了9S
新增調(diào)度任務(wù)
刪除調(diào)度任務(wù)
以上就是SpringBoot定時任務(wù)動態(tài)擴展ScheduledTaskRegistrar詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot ScheduledTaskRegistrar的資料請關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot中定時任務(wù)@Scheduled的多線程使用詳解
- SpringBoot通過@Scheduled實現(xiàn)定時任務(wù)及單線程運行問題解決
- SpringBoot?ScheduledTaskRegistrar解決動態(tài)定時任務(wù)思路詳解
- SpringBoot中定時任務(wù)@Scheduled注解的使用解讀
- 解決SpringBoot中的Scheduled單線程執(zhí)行問題
- SpringBoot中使用@scheduled定時執(zhí)行任務(wù)的坑
- springboot使用定時器@Scheduled不管用的解決
- 帶你3分鐘帶你搞定Spring Boot中Schedule
相關(guān)文章
java高并發(fā)的volatile與Java內(nèi)存模型詳解
這篇文章主要介紹了java高并發(fā)的volatile與Java內(nèi)存模型,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2021-10-10springmvc的validator數(shù)據(jù)校驗的實現(xiàn)示例代碼
這篇文章主要介紹了springmvc的數(shù)據(jù)校驗的實現(xiàn)示例代碼, 具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07Java為何需要平衡方法調(diào)用與內(nèi)聯(lián)
這篇文章主要介紹了Java為何需要平衡方法調(diào)用與內(nèi)聯(lián),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01