1.java中的定时定任务调度之Timer定时器(案例和源码分析)
2.Go 语言一次性定时器使用方式和实现原理
3.如何实现定时任务- Java Timer/TimerTask 源码解析
4.xxl-job定时任务触发实现分析
5.nodejs 14.0.0源码分析之setTimeout
6.Timer & TimerTask 源码分析
java中的任务调度之Timer定时器(案例和源码分析)
定时器在日常生活中如同闹钟般常见,用于在特定时间执行任务或重复执行同一任务。触发触在Java中,源码源码用内置的定时定定时任务器 Timer 是实现此功能的强大工具。本文将深入探讨 Timer 的触发触基本使用、源码分析及其局限性。源码源码用友多多系统源码 一、定时定Timer 基本使用 在 Java 中,触发触通过 Timer 实现定时任务时,源码源码用主要涉及到 Timer 和 TimerTask 这两个类。定时定Timer 负责管理任务的触发触执行,而 TimerTask 则包含具体任务的源码源码用实现。使用步骤如下: 1. 创建 Timer。定时定 2. 创建 TimerTask 并实现业务逻辑。触发触 3. 使用 Timer 的源码源码用 schedule 方法执行 TimerTask,可以指定开始执行时间、间隔时间等参数。 例如,创建一个在 2 秒后执行、每隔 1 秒执行一次的 TimerTask: javaTimer timer = new Timer();
TimerTask myTask = new MyTask();
timer.schedule(myTask, L, L);
二、Timer 源码分析 深入剖析 Timer 的源码有助于理解其内部机制。Timer 类内部包含 TaskQueue 和 TimerThread 两个关键组件。 1. **TaskQueue**:这是一个最小堆,存放 Timer 的所有 TimerTask。根据每个 TimerTask 的 nextExecutionTime(下次执行开始时间)决定其在堆中的位置。nextExecutionTime 越小,任务越有可能先执行。 2. **TimerThread**:执行 TaskQueue 中的任务后,将任务从队列中移除。 TimerTask 的位置决定于其 nextExecutionTime,确保优先执行执行时间最早的任务。此外,Timer 默认大小为 个任务。 构造方法包括默认构造、是诛仙公告源码否为守护线程、带名字的构造、带名字和是否为守护线程的构造。 定时任务方法包括: 1. schedule(task, time):在时间等于或超过 time 时执行 task 且仅执行一次。 2. schedule(task, time, period):首次在 time 时执行 task,之后每隔 period 毫秒重复执行。 3. schedule(task, delay):在 delay 时间后执行 task 且仅执行一次。 4. schedule(task, delay, period):在 delay 后开始首次执行 task,之后每隔 period 毫秒重复执行。 执行定时任务的核心在于队列的维护和优先级调度。此外,还存在 scheduleAtFixedRate 方法,其行为与 scheduleAtFixedRate 类似,但考虑了任务执行所需时间的并发性。 三、Timer 缺陷 尽管 Timer 提供了基本的定时任务功能,但存在一些局限性: 1. **线程管理不足**:当多个任务执行时间过长,且时间间隔不一致时,可能会导致任务执行顺序与预期不符,影响任务调度效率。 2. **异常处理机制**:当 TimerTask 抛出 RuntimeException,所有任务都会停止执行,缺乏异常恢复机制。 为了克服这些缺陷,出现了更高级的 Timer 替代品 ScheduledExecutorService,以及众多优秀的框架,提供更强大的任务管理和执行能力。未来文章中将详细介绍这些工具及其优势。Go 语言一次性定时器使用方式和实现原理
在 Go 语言的标准库time包中,有一个名为Timer的类型,它代表了一个单一事件的计时器,即一次性定时器。
在Go语言的项目开发中,定时器的使用非常普遍。本文将向大家介绍如何在Go语言中使用Timer,淘宝好店 源码以及其背后的实现原理。
要使用Timer一次性定时器,首先需要导入time包。创建Timer的方式有两种:
func NewTimer(d Duration) *Timer
使用func NewTimer创建Timer时,需要传入定时器的等待时间。时间到达时,会向channel中发送当前时间。
示例代码:
通过阅读上面的代码,我们可以看到我们定义了一个2秒后执行的定时器timer,然后使用select读取timer.C中的数据。当读取到数据时,会执行特定的业务逻辑代码。
func AfterFunc(d Duration, f func()) *Timer
使用func AfterFunc创建Timer时,需要传入定时器的等待时间和时间到达时执行的函数。
示例代码:
细心的读者可能已经发现,在代码末尾我们使用了time.Sleep(),这是因为time.AfterFunc()是异步执行的,所以需要等待协程退出。
在Timer的源码中,我们可以看到一个数据结构,它包含两个字段:一个是可导出字段C,这是一个Time类型的channel;另一个是不可导出字段r,这是一个runtimeTimer类型。
实际上,每个Go应用程序底层都会有一个特定的协程来管理Timer。当监控到某个Timer指定的时间到达时,这个协程会将当前时间发送到C中,然后上层读取到C中的数据时,执行相关的业务逻辑代码。
底层协程会监控Timer的r字段中的数据。在源码中查看runtimeTimer的数据结构,我们可以发现其中包含的所有字段。重点了解when、f和arg。2018授权系统源码
在简单了解Timer的数据结构后,我们查看func NewTimer的代码,可以看到它的实现非常简单。它实际上就是构造了一个Timer,然后把Timer.r传参给startTimer(),除了startTimer()函数外,还有两个函数,分别是when()和sendTime,其中when()是计算计时器的执行时间,sendTime是计时器时间到达时执行的事件(实际上就是将当前时间写入通道中)。
sendTime源码:
我们已经了解到,func NewTimer将构造的Timer.r传参给startTimer(),它负责将runtimeTimer写入底层协程的数组中(如果底层协程未运行,它将会启动底层协程),将Timer交给底层协程监控。也就是说,当底层协程监控到某个Timer指定时间到达时,将当前时间发送到它的通道中。
本文介绍了Go语言标准库time包提供的一次性定时器Timer,不仅介绍了它的使用方式,还介绍了它的实现原理。
限于篇幅,本文没有介绍Stop()和Reset()方法,感兴趣的读者可以查阅相关资料。
如何实现定时任务- Java Timer/TimerTask 源码解析
日常实现各种服务端系统时,我们一定会有一些定时任务的需求。比如会议提前半小时自动提醒,异步任务定时/周期执行等。那么如何去实现这样的一个定时任务系统呢? Java JDK提供的Timer类就是一个很好的工具,通过简单的API调用,我们就可以实现定时任务。
现在就来看一下java.util.Timer是如何实现这样的定时功能的。
首先,我们来看一下一个使用demo
基本的图文管理系统源码使用方法:
加入任务的API如下:
可以看到API方法内部都是调用sched方法,其中time参数下一次任务执行时间点,是通过计算得到。period参数为0的话则表示为一次性任务。
那么我们来看一下Timer内部是如何实现调度的。
内部结构
先看一下Timer的组成部分:
Timer有3个重要的模块,分别是 TimerTask, TaskQueue, TimerThread
那么,在加入任务之后,整个Timer是怎么样运行的呢?可以看下面的示意图:
图中所示是简化的逻辑,多个任务加入到TaskQueue中,会自动排序,队首任务一定是当前执行时间最早的任务。TimerThread会有一个一直执行的循环,从TaskQueue取队首任务,判断当前时间是否已经到了任务执行时间点,如果是则执行任务。
工作线程
流程中加了一些锁,用来避免同时加入TimerTask的并发问题。可以看到sched方法的逻辑比较简单,task赋值之后入队,队列会自动按照nextExecutionTime排序(升序,排序的实现原理后面会提到)。
从mainLoop的源码中可以看出,基本的流程如下所示
当发现是周期任务时,会计算下一次任务执行的时间,这个时候有两种计算方式,即前面API中的
优先队列
当从队列中移除任务,或者是修改任务执行时间之后,队列会自动排序。始终保持执行时间最早的任务在队首。 那么这是如何实现的呢?
看一下TaskQueue的源码就清楚了
可以看到其实TaskQueue内部就是基于数组实现了一个最小堆 (balanced binary heap), 堆中元素根据 执行时间nextExecutionTime排序,执行时间最早的任务始终会排在堆顶。这样工作线程每次检查的任务就是当前最早需要执行的任务。堆的初始大小为,有简单的倍增扩容机制。
TimerTask 任务有四种状态:
Timer 还提供了cancel和purge方法
常见应用
Java的Timer广泛被用于实现异步任务系统,在一些开源项目中也很常见, 例如消息队列RocketMQ的 延时消息/消费重试 中的异步逻辑。
上面这段代码是RocketMQ的延时消息投递任务 ScheduleMessageService 的核心逻辑,就是使用了Timer实现的异步定时任务。
不管是实现简单的异步逻辑,还是构建复杂的任务系统,Java的Timer确实是一个方便实用,而且又稳定的工具类。从Timer的实现原理,我们也可以窥见定时系统的一个基础实现:线程循环 + 优先队列。这对于我们自己去设计相关的系统,也会有一定的启发。
xxl-job定时任务触发实现分析
在深入探讨《调度中心启动流程分析》中的调度任务启动源码时,重点解析了JobScheduleHelper类的start方法。此方法启动了两个关键线程:scheduleThread和ringThread。具体而言,scheduleThread线程和ringThread线程的run方法包含调度任务执行的逻辑。在分析这两个线程的运行机制前,提出以下两个问题以加深理解:
在xxl-job集群部署中,如何有效防止多个服务器同时调度任务?
在xxl-job集群部署时,避免多个服务器同时调度任务通过使用MySQL的悲观锁实现分布式锁。这确保了在任何时候,只有一个服务器能够访问和更新xxl_job_info表,有效防止了并发调度冲突。
如何实现定时任务触发机制?
定时任务的实现流程主要涉及定时任务的存储与调度。xxl-job通过xxl_job_info表记录定时任务信息,特别是下一次执行的时间戳。定时任务每执行一次,数据库中的该时间戳更新,以确定下一次触发时间。任务调度过程如下:周期性地从数据库中读取5秒内的任务,根据下一次触发时间决定是否执行,同时任务被放入时间轮中,由ringThread线程进行任务调度。
时间轮的概念及其在xxl-job中的应用。时间轮将一段时间分割为等分时间段,每个时间段关联对应任务。具体实现中,ringData结构以秒数(1-)为key,任务ID列表为value。ringThread线程的run方法通过获取当前秒数及前两个秒数的任务列表,触发任务执行,并清空已执行任务列表。此设计旨在避免长时间任务处理导致错过调度时机。
总结xxl-job的定时任务调度机制,核心在于时间轮的高效管理,结合数据库查询和线程调度策略,确保任务按计划有序执行。本文主要分析了定时任务的启动与调度流程,未来将会探讨任务执行的详细流程。通过解析上述内容,xxl-job提供了一种灵活、高效的任务调度解决方案,适用于大规模并发场景。
nodejs .0.0源码分析之setTimeout
本文深入剖析了Node.js .0.0版中定时器模块的实现机制。在.0.0版本中,Node.js 对定时器模块进行了重构,改进了其内部结构以提高性能和效率。下面将详细介绍定时器模块的关键组成部分及其实现细节。 首先,让我们了解一下定时器模块的组织结构。Node.js 采用了链表和优先队列(二叉堆)的组合来管理定时器。链表用于存储具有相同超时时间的定时器,而优先队列则用来高效地管理这些链表。 链表通过 TimersList数据结构进行管理,它允许将具有相同超时时间的定时器归类到同一队列中。这样,Node.js 能够快速定位并处理即将到期的定时器。 为了进一步优化性能,Node.js 使用了一个优先队列(二叉堆)来管理所有链表。在这个队列中,每个链表对应一个节点,根节点表示最快到期的定时器。在时间循环(timer阶段)时,Node.js 会从二叉堆中查找超时的节点,并执行相应的回调函数。 为了实现这一功能,Node.js 还维护了一个超时时间到链表的映射,以确保快速访问和管理定时器。 接下来,我们将从 setTimeout函数的实现开始分析。这个函数主要涉及 new Timeout和 insert两个操作。其中,new Timeout用于创建一个对象来存储定时器的上下文信息,而 insert函数则用于将定时器插入到优先队列中。 具体地,Node.js 使用了 scheduleTimer函数来封装底层计时操作。这个函数通过将定时器插入到libuv的二叉堆中,为每个定时器指定一个超时时间(即最快的到期时间)。在执行时间循环时,libuv会根据这个时间判断是否需要触发定时器。 当定时器触发时,Node.js 会调用 RunTimers函数来执行回调。回调函数是在Node.js初始化时设置的,负责处理定时器触发时的具体逻辑。在回调函数中,Node.js 遍历优先队列以检查是否有其他未到期的定时器,并相应地更新libuv定时器的时间。 最后,Node.js 在初始化时通过设置 processTimers函数作为超时回调来确保定时器的正确执行。通过这种方式,Node.js 保证了定时器模块的初始化和定时器触发时的执行逻辑。 本文通过详尽的分析,展示了Node.js .0.0版中定时器模块的内部机制,包括其组织结构、数据管理和回调处理等关键方面。虽然本文未涵盖所有细节,但对于理解Node.js定时器模块的实现原理提供了深入的洞察。对于进一步探索Node.js定时器模块的实现,特别是与libuv库的交互,后续文章将提供更详细的分析。Timer & TimerTask 源码分析
尽管 Timer 已经在现代 Java 开发中鲜少使用,但其内部结构对理解和实现自动化流程有着重要参考价值。这篇源码分析着重于 Timer 和 TimerTask 的工作原理,它们通过维护一个 TaskQueue,确保任务按照预设时间执行,其中的并发处理策略对初学者极具启发性。
在 Timer 类中,每个 Timer 实例对应一个单独的线程,这可能导致任务执行顺序受阻。Timer 的生命周期不确定,任务完成后可能不会立即回收,而 ScheduledThreadPoolExecutor 是推荐的替代方案。Timer 是线程安全的,但不保证任务执行的实时性,而是依赖于 wait() 等待机制。TaskQueue 是 TimerThread 的核心,它负责调度任务的执行。
TimerThread 是负责执行任务的线程,继承自 Thread,其简洁的实现表明了其功能的专注。Timer 的构造器和 schedule 方法提供多种重载形式,而 sched 方法是它们的最终调用者。TimerTask 是一个抽象类,实现了 Runnable,用户需创建其子类并覆盖 run 方法,定义了任务的状态标识和执行时间属性。
尽管 Timer 已经过时,但理解其内部机制有助于在需要定时任务的场景中找到更高效、可靠的解决方案。
SpringBoot动态定时任务的实现
1. Spring 定时任务的简单实现
在Spring Boot中使用定时任务,只需要@EnableScheduling开启定时任务支持,在需要调度的方法上添加@Scheduled注解。这样就能够在项目中开启定时调度功能了,支持通过cron、fixedRate、fixedDelay等灵活的控制执行周期和频率。
1.1 缺点周期一旦指定,想要更改必须要重启应用
1.2 需求热更新定时任务的执行周期,基于cron表达式并支持外部存储,如数据库,nacos等
最小改造兼容现有的定时任务(仅需添加一个注解)
动态增加定时任务
2.Spring 定时任务源码分析2.1 @EnableScheduling 引入了配置类 SchedulingConfiguration
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(SchedulingConfiguration.class)@Documentedpublic@interfaceEnableScheduling{ }2.2 SchedulingConfiguration只配置了一个bean,ScheduledAnnotationBeanPostProcessor从名字就知道该类实现BeanPostProcessor接口
@Configuration@Role(BeanDefinition.ROLE_INFRASTRUCTURE)publicclassSchedulingConfiguration{ @Bean(name=TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)publicScheduledAnnotationBeanPostProcessorscheduledAnnotationProcessor(){ returnnewScheduledAnnotationBeanPostProcessor();}}2.3 ScheduledAnnotationBeanPostProcessor的postProcessAfterInitialization实现,可见具体处理@Scheduled实现定时任务的是processScheduled方法
@OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName){ if(beaninstanceofAopInfrastructureBean||beaninstanceofTaskScheduler||beaninstanceofScheduledExecutorService){ //IgnoreAOPinfrastructuresuchasscopedproxies.returnbean;}Class<?>targetClass=AopProxyUtils.ultimateTargetClass(bean);if(!this.nonAnnotatedClasses.contains(targetClass)&&AnnotationUtils.isCandidateClass(targetClass,Arrays.asList(Scheduled.class,Schedules.class))){ //获取bean的方法及@Scheduled映射关系Map<Method,Set<Scheduled>>annotatedMethods=MethodIntrospector.selectMethods(targetClass,(MethodIntrospector.MetadataLookup<Set<Scheduled>>)method->{ Set<Scheduled>scheduledMethods=AnnotatedElementUtils.getMergedRepeatableAnnotations(method,Scheduled.class,Schedules.class);return(!scheduledMethods.isEmpty()?scheduledMethods:null);});if(annotatedMethods.isEmpty()){ this.nonAnnotatedClasses.add(targetClass);if(logger.isTraceEnabled()){ logger.trace("No@Scheduledannotationsfoundonbeanclass:"+targetClass);}}else{ //Non-emptysetofmethodsannotatedMethods.forEach((method,scheduledMethods)->//处理@Scheduled注解scheduledMethods.forEach(scheduled->processScheduled(scheduled,method,bean)));if(logger.isTraceEnabled()){ logger.trace(annotatedMethods.size()+"@Scheduledmethodsprocessedonbean'"+beanName+"':"+annotatedMethods);}}}returnbean;}2.4 以下仅贴出ScheduledAnnotationBeanPostProcessor.processScheduled处理cron表达式的关键实现,
privatefinalScheduledTaskRegistrarregistrar;publicScheduledAnnotationBeanPostProcessor(){ this.registrar=newScheduledTaskRegistrar();}protectedvoidprocessScheduled(Scheduledscheduled,Methodmethod,Objectbean){ try{ //将定时任务方法,转为RunnableRunnablerunnable=createRunnable(bean,method);booleanprocessedSchedule=false;Set<ScheduledTask>tasks=newLinkedHashSet<>(4);//Determineinitialdelay//处理scheduled.initialDelay()的值,略过...//CheckcronexpressionStringcron=scheduled.cron();if(StringUtils.hasText(cron)){ Stringzone=scheduled.zone();if(this.embeddedValueResolver!=null){ //${ }变量值表达式的转换cron=this.embeddedValueResolver.resolveStringValue(cron);zone=this.embeddedValueResolver.resolveStringValue(zone);}if(StringUtils.hasLength(cron)){ Assert.isTrue(initialDelay==-1,"'initialDelay'notsupportedforcrontriggers");processedSchedule=true;if(!Scheduled.CRON_DISABLED.equals(cron)){ TimeZonetimeZone;if(StringUtils.hasText(zone)){ timeZone=StringUtils.parseTimeZoneString(zone);}else{ timeZone=TimeZone.getDefault();}//创建cron触发器CronTrigger对象,并注册CronTasktasks.add(this.registrar.scheduleCronTask(newCronTask(runnable,newCronTrigger(cron,timeZone))));}}}//处理fixedDelay和fixedRate,及ScheduledTask保存用于销毁,略过...}//略过catchException...}以上通过this.registrar.scheduleCronTask实现cron定时任务注册或初始化
3.动态定时任务的实现实现思路: 重写ScheduledAnnotationBeanPostProcessor.processScheduled方法,修改处理cron的部分代码,使用this.registrar.scheduleTriggerTask注册或初始化定时任务
3.1 相关类图classDiagramDisposableBean<|--DynamicCronScheduleTaskManagerEnvironmentAware<|--EnvironmentDynamicCronHandlerAbstractDynamicCronHandler<|--EnvironmentDynamicCronHandlerTrigger<|--DynamicCronTriggerEnvironmentAware:+setEnvironment()DisposableBean:+destroy()voidTrigger:+nextExecutionTime(TriggerContexttriggerContext)DateclassDynamicCronScheduleTaskManager{ +Map<String,ScheduledTask>dynamicScheduledTaskMap-ScheduledTaskRegistrarregistrar+addTriggerTask(StringcronName,TriggerTasktask)ScheduledTask+contains(StringcronName)boolean+updateTriggerTask(StringcronName)void+removeTriggerTask(StringcronName)void}classAbstractDynamicCronHandler{ -DynamicCronScheduleTaskManagerdynamicCronScheduleTaskManager;+getCronExpression(StringcronName)String+updateTriggerTash(StringcronName)void}classEnvironmentDynamicCronHandler{ +Environmentenvironment+environmentChangeEvent(EnvironmentChangeEventevent)void}classDynamicCronTrigger{ -StringcronName-AbstractDynamicCronHandlerdynamicCronHandler-StringcronExpression-CronSequenceGeneratorsequenceGenerator}classScheduledDynamicCron{ +value()String+cronName()String+handler()Class<?extendsAbstractDynamicCronHandler>}3.2 DynamicCronScheduleTaskManagerimportorg.springframework.beans.factory.DisposableBean;importorg.springframework.scheduling.config.ScheduledTask;importorg.springframework.scheduling.config.ScheduledTaskRegistrar;importorg.springframework.scheduling.config.TriggerTask;importjava.util.HashMap;importjava.util.Map;/***@authorHuangJS*@date--:下午*/publicclassDynamicCronScheduleTaskManagerimplementsDisposableBean{ privateMap<String,ScheduledTask>dynamicScheduledTaskMap=newHashMap<>();ScheduledTaskRegistrarregistrar;//添加定时任务publicScheduledTaskaddTriggerTask(StringcronName,TriggerTasktask){ ScheduledTaskscheduledTask=dynamicScheduledTaskMap.get(cronName);if(scheduledTask!=null){ scheduledTask.cancel();}scheduledTask=this.registrar.scheduleTriggerTask(task);dynamicScheduledTaskMap.put(cronName,scheduledTask);returnscheduledTask;}publicbooleancontains(StringcronName){ returnthis.dynamicScheduledTaskMap.containsKey(cronName);}//更新定时任务的触发时机publicvoidupdateTriggerTask(StringcronName){ ScheduledTaskscheduledTask=dynamicScheduledTaskMap.get(cronName);if(scheduledTask==null){ thrownewIllegalStateException("InvalidcronName""+cronName+"",nofundScheduledTask");}scheduledTask.cancel();scheduledTask=this.registrar.scheduleTriggerTask((TriggerTask)scheduledTask.getTask());dynamicScheduledTaskMap.put(cronName,scheduledTask);}//移除定时任务publicvoidremoveTriggerTask(StringcronName){ ScheduledTaskscheduledTask=dynamicScheduledTaskMap.remove(cronName);if(scheduledTask!=null){ scheduledTask.cancel();}}@Overridepublicvoiddestroy()throwsException{ for(ScheduledTaskvalue:dynamicScheduledTaskMap.values()){ value.cancel();}this.dynamicScheduledTaskMap.clear();}}3.3 AbstractDynamicCronHandlerpublicabstractclassAbstractDynamicCronHandler{ @AutowiredprivateDynamicCronScheduleTaskManagerdynamicCronScheduleTaskManager;/***获取cron表达式*@return*/publicabstractStringgetCronExpression(StringcronName);/***更新cronName对应的定时任务的触发时机*@paramcronName*/publicvoidupdateTriggerTask(StringcronName){ dynamicCronScheduleTaskManager.updateTriggerTask(cronName);}}3.4 EnvironmentDynamicCronHandler基于Environment,在刷新配置时,自动刷新定时任务的触发时机,支持分布式多节点集群部署。
如,cron表达式配置在nacos,更新nacos上的配置时由于监听了EnvironmentChangeEvent事件实现了定时任务的触发时机的更新
importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.cloud.context.environment.EnvironmentChangeEvent;importorg.springframework.context.EnvironmentAware;importorg.springframework.context.event.EventListener;importorg.springframework.core.env.Environment;/***@authorHuangJS*@date--:上午*/publicclassEnvironmentDynamicCronHandlerextendsAbstractDynamicCronHandlerimplementsEnvironmentAware{ privatefinalLoggerlogger=LoggerFactory.getLogger(EnvironmentDynamicCronHandler.class);privateEnvironmentenvironment;@OverridepublicStringgetCronExpression(StringcronName){ try{ returnenvironment.getProperty(cronName);}catch(Exceptione){ logger.error(e.getMessage(),e);}returnnull;}@OverridepublicvoidsetEnvironment(Environmentenvironment){ this.environment=environment;}@EventListenerpublicvoidenvironmentChangeEvent(EnvironmentChangeEventevent){ for(Stringkey:event.getKeys()){ if(this.dynamicCronScheduleTaskManager.contains(key)){ this.dynamicCronScheduleTaskManager.updateTriggerTask(key);}}}}3.5 DynamicCronTriggerpublicclassDynamicCronTriggerimplementsTrigger{ privatefinalstaticLoggerLOGGER=LoggerFactory.getLogger(DynamicCronTrigger.class);privateStringcronName;privateAbstractDynamicCronHandlerdynamicCronHandler;privateStringcronExpression;privateCronSequenceGeneratorsequenceGenerator;publicDynamicCronTrigger(StringcronName,AbstractDynamicCronHandlerdynamicCronHandler){ this.cronName=cronName;this.dynamicCronHandler=dynamicCronHandler;}@OverridepublicDatenextExecutionTime(TriggerContexttriggerContext){ StringcronExpression=dynamicCronHandler.getCronExpression(cronName);if(cronExpression==null){ returnnull;}if(this.sequenceGenerator==null||!cronExpression.equals(this.cronExpression)){ try{ this.sequenceGenerator=newCronSequenceGenerator(cronExpression);this.cronExpression=cronExpression;}catch(Exceptione){ LOGGER.error(e.getMessage(),e);}}Datedate=triggerContext.lastCompletionTime();if(date!=null){ Datescheduled=triggerContext.lastScheduledExecutionTime();if(scheduled!=null&&date.before(scheduled)){ //Previoustaskapparentlyexecutedtooearly...//Let'ssimplyusethelastcalculatedexecutiontimethen,//inordertopreventaccidentalre-firesinthesamesecond.date=scheduled;}}else{ date=newDate();}returnthis.sequenceGenerator.next(date);}}3.6 注解类ScheduledDynamicCron@Target({ ElementType.METHOD,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceScheduledDynamicCron{ /***动态cron名称*@return*/@AliasFor("cronName")Stringvalue()default"";/***动态cr