线程池相关--Spring定时任务执行原理分析
最近在系统学习线程池,看到定时任务线程池ScheduleThreadPoolExecutor联想到了Spring也有定时任务,而且他们执行的现象都很像,都需要等待前面的线程执行完才能执行后面的线程,所以想去翻一下源码看看是不是和猜测的一样。
/***有三种方式cronfixedDelayfixedRate,要在SpringApplication入口处加注解EnableScheduling才生效,spring才会去开启定时任务。*Annotationthatmarksamethodtobescheduled.Exactlyoneofthe*{@link#cron},{@link#fixedDelay},or{@link#fixedRate}attributesmustbe*specified.**<p>Theannotatedmethodmustexpectnoarguments.Itwilltypicallyhave*a{@codevoid}returntype;ifnot,thereturnedvaluewillbeignored*whencalledthroughthescheduler.**<p>Processingof{@code@Scheduled}annotationsisperformedby*registeringa{@linkScheduledAnnotationBeanPostProcessor}.Thiscanbe*donemanuallyor,moreconveniently,throughthe{@code<task:annotation-driven/>}*elementor@{@linkEnableScheduling}annotation.**<p>Thisannotationmaybeusedasa<em>meta-annotation</em>tocreatecustom*<em>composedannotations</em>withattributeoverrides.**@authorMarkFisher*@authorJuergenHoeller*@authorDaveSyer*@authorChrisBeams*@since3.0*@seeEnableScheduling*@seeScheduledAnnotationBeanPostProcessor*@seeSchedules*/@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Repeatable(Schedules.class)public@interfaceScheduled{/***Aspecialcronexpressionvaluethatindicatesadisabledtrigger:{@value}.*<p>Thisisprimarilymeantforusewith<code>${...}</code>placeholders,*allowingforexternaldisablingofcorrespondingscheduledmethods.*@since5.1*@seeScheduledTaskRegistrar#CRON_DISABLED*/StringCRON_DISABLED=ScheduledTaskRegistrar.CRON_DISABLED;/**cron表达式*Acron-likeexpression,extendingtheusualUN*Xdefinitiontoincludetriggers*onthesecond,minute,hour,dayofmonth,month,anddayofweek.*<p>Forexample,{@code"0****MON-FRI"}meansonceperminuteonweekdays*(atthetopoftheminute-the0thsecond).*<p>Thefieldsreadfromlefttorightareinterpretedasfollows.*<ul>*<li>second</li>*<li>minute</li>*<li>hour</li>*<li>dayofmonth</li>*<li>month</li>*<li>dayofweek</li>*</ul>*<p>Thespecialvalue{@link#CRON_DISABLED"-"}indicatesadisabledcron*trigger,primarilymeantforexternallyspecifiedvaluesresolvedbya*<code>${...}</code>placeholder.*@returnanexpressionthatcanbeparsedtoacronschedule*@seeorg.springframework.scheduling.support.CronExpression#parse(String)*/Stringcron()default"";/**cron的时区,默认是本地的*Atimezoneforwhichthecronexpressionwillberesolved.Bydefault,this*attributeistheemptyString(i.e.theserver'slocaltimezonewillbeused).*@returnazoneidacceptedby{@linkjava.util.TimeZone#getTimeZone(String)},*oranemptyStringtoindicatetheserver'sdefaulttimezone*@since4.0*@seeorg.springframework.scheduling.support.CronTrigger#CronTrigger(String,java.util.TimeZone)*@seejava.util.TimeZone*/Stringzone()default"";/***Executetheannotatedmethodwithafixedperiodinmillisecondsbetweenthe*endofthelastinvocationandthestartofthenext.*@returnthedelayinmilliseconds*/longfixedDelay()default-1;/**固定间隔周期运行,如果执行任务时间超过一个周期,那么就在下一个周期开始。其实就是和ScheduledThreadPoolExecutor.scheduleWithFixedDelay()一样的*Executetheannotatedmethodwithafixedperiodinmillisecondsbetweenthe*endofthelastinvocationandthestartofthenext.*@returnthedelayinmillisecondsasaStringvalue,e.g.aplaceholder*ora{@linkjava.time.Duration#parsejava.time.Duration}compliantvalue*@since3.2.2*/StringfixedDelayString()default"";/**固定间隔周期运行,如果超过了间隔周期就马上执行*Executetheannotatedmethodwithafixedperiodinmillisecondsbetween*invocations.*@returntheperiodinmilliseconds*/longfixedRate()default-1;/***Executetheannotatedmethodwithafixedperiodinmillisecondsbetween*invocations.*@returntheperiodinmillisecondsasaStringvalue,e.g.aplaceholder*ora{@linkjava.time.Duration#parsejava.time.Duration}compliantvalue*@since3.2.2*/StringfixedRateString()default"";/**初始时延迟时间*Numberofmillisecondstodelaybeforethefirstexecutionofa*{@link#fixedRate}or{@link#fixedDelay}task.*@returntheinitialdelayinmilliseconds*@since3.2*/longinitialDelay()default-1;/***Numberofmillisecondstodelaybeforethefirstexecutionofa*{@link#fixedRate}or{@link#fixedDelay}task.*@returntheinitialdelayinmillisecondsasaStringvalue,e.g.aplaceholder*ora{@linkjava.time.Duration#parsejava.time.Duration}compliantvalue*@since3.2.2*/StringinitialDelayString()default"";}
需要执行定时任务的方法加注解@Scheduled,该注解的入口是在ScheduledAnnotationBeanPostProcessor.java里面,怎么找到可查看Spring boot的启动流程分析。
测试代码
@Component@Slf4jpublicclassMyTask{privateList<Integer>index=Arrays.asList(8*1000,3*1000,6*1000,2*1000,2*1000);privateAtomicIntegeratomicInteger=newAtomicInteger(0);//@Scheduled(/*initialDelay=1000,*/fixedRate=5*1000)//@Scheduled(/*initialDelay=1000,*/fixedDelay=5*1000)@Scheduled(cron="0/5****?")publicvoidtestScheduleCron()throwsInterruptedException{inti=atomicInteger.get();if(i<5){IntegersleepTime=index.get(i);log.info("第{}个任务开始执行,执行时间为{}ms",i,sleepTime);Thread.sleep(sleepTime);atomicInteger.getAndIncrement();}}}
注解带入了要执行的目前对象和方法
初始延迟间隔时间
cron表达式逻辑
fixed delay逻辑
fixed rate逻辑
继续追
tasks.add(this.registrar.scheduleCronTask(newCronTask(runnable,newCronTrigger(cron,timeZone))));tasks.add(this.registrar.scheduleFixedDelayTask(newFixedDelayTask(runnable,fixedDelay,initialDelay)));tasks.add(this.registrar.scheduleFixedDelayTask(newFixedDelayTask(runnable,fixedDelay,initialDelay)));
原来是用Executors.newSingleThreadScheduledExecutor()
借肥朝的图片
cron
每隔一个周期去检查是没任务执行,没有就执行,如果有只能等下一个周期到了再检查一次。看起来有点笨,但是理解起来简单。而且cron表达式比较丰富
fixed Delay
上一个任务执行完,等一个周期后再执行
fixed Rate
这个就不好描述了,如果上一个任务执行完了,还没超过间隔周期,就等到原本按照周期的时间再开始下一个任务,这时间是从第一个任务开始算的。
如果上一个任务执行超过了间隔时间,下一个任务就立刻执行。
cron表达式实现里面用了Tigger
具体实现是 CronTrigger.java
@OverridepublicDatenextExecutionTime(TriggerContexttriggerContext){Datedate;DatelastDate=triggerContext.lastCompletionTime();if(lastDate!=null){Datescheduled=triggerContext.lastScheduledExecutionTime();//这个ifelse很好理解,如果上一个完成时间早于预计执行时间,那么按照计划来//否则立刻开始if(scheduled!=null&&lastDate.before(scheduled)){//Previoustaskapparentlyexecutedtooearly...//Let'ssimplyusethelastcalculatedexecutiontimethen,//inordertopreventaccidentalre-firesinthesamesecond.date=scheduled;}}else{date=newDate();}ZonedDateTimedateTime=ZonedDateTime.ofInstant(date.toInstant(),this.zoneId);ZonedDateTimenext=this.expression.next(dateTime);returnnext!=null?Date.from(next.toInstant()):null;}
这里提到了定时任务管理,有Trigger概念。记得定时任务管理组件 如 xxl-job、elastic-job里面也有触发器的概念,下次对比一下。