线程池相关--Spring定时任务执行原理分析
最近在系统学习线程池,看到定时任务线程池ScheduleThreadPoolExecutor联想到了Spring也有定时任务,而且他们执行的现象都很像,都需要等待前面的线程执行完才能执行后面的线程,所以想去翻一下源码看看是不是和猜测的一样。
/**
* 有三种方式 cron fixedDelay fixedRate,要在SpringApplication入口处加注解EnableScheduling才生效,spring才会去开启定时任务。
* Annotation that marks a method to be scheduled. Exactly one of the
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes must be
* specified.
*
* <p>The annotated method must expect no arguments. It will typically have
* a {@code void} return type; if not, the returned value will be ignored
* when called through the scheduler.
*
* <p>Processing of {@code @Scheduled} annotations is performed by
* registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
* done manually or, more conveniently, through the {@code <task:annotation-driven/>}
* element or @{@link EnableScheduling} annotation.
*
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
* <em>composed annotations</em> with attribute overrides.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Dave Syer
* @author Chris Beams
* @since 3.0
* @see EnableScheduling
* @see ScheduledAnnotationBeanPostProcessor
* @see Schedules
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
/**
* A special cron expression value that indicates a disabled trigger: {@value}.
* <p>This is primarily meant for use with <code>${...}</code> placeholders,
* allowing for external disabling of corresponding scheduled methods.
* @since 5.1
* @see ScheduledTaskRegistrar#CRON_DISABLED
*/
String CRON_DISABLED = ScheduledTaskRegistrar.CRON_DISABLED;
/** cron表达式
* A cron-like expression, extending the usual UN*X definition to include triggers
* on the second, minute, hour, day of month, month, and day of week.
* <p>For example, {@code "0 * * * * MON-FRI"} means once per minute on weekdays
* (at the top of the minute - the 0th second).
* <p>The fields read from left to right are interpreted as follows.
* <ul>
* <li>second</li>
* <li>minute</li>
* <li>hour</li>
* <li>day of month</li>
* <li>month</li>
* <li>day of week</li>
* </ul>
* <p>The special value {@link #CRON_DISABLED "-"} indicates a disabled cron
* trigger, primarily meant for externally specified values resolved by a
* <code>${...}</code> placeholder.
* @return an expression that can be parsed to a cron schedule
* @see org.springframework.scheduling.support.CronExpression#parse(String)
*/
String cron() default "";
/** cron的时区,默认是本地的
* A time zone for which the cron expression will be resolved. By default, this
* attribute is the empty String (i.e. the server's local time zone will be used).
* @return a zone id accepted by {@link java.util.TimeZone#getTimeZone(String)},
* or an empty String to indicate the server's default time zone
* @since 4.0
* @see org.springframework.scheduling.support.CronTrigger#CronTrigger(String, java.util.TimeZone)
* @see java.util.TimeZone
*/
String zone() default "";
/**
* Execute the annotated method with a fixed period in milliseconds between the
* end of the last invocation and the start of the next.
* @return the delay in milliseconds
*/
long fixedDelay() default -1;
/**固定间隔周期运行,如果执行任务时间超过一个周期,那么就在下一个周期开始。其实就是和ScheduledThreadPoolExecutor.scheduleWithFixedDelay()一样的
* Execute the annotated method with a fixed period in milliseconds between the
* end of the last invocation and the start of the next.
* @return the delay in milliseconds as a String value, e.g. a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
*/
String fixedDelayString() default "";
/**固定间隔周期运行,如果超过了间隔周期就马上执行
* Execute the annotated method with a fixed period in milliseconds between
* invocations.
* @return the period in milliseconds
*/
long fixedRate() default -1;
/**
* Execute the annotated method with a fixed period in milliseconds between
* invocations.
* @return the period in milliseconds as a String value, e.g. a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
*/
String fixedRateString() default "";
/** 初始时延迟时间
* Number of milliseconds to delay before the first execution of a
* {@link #fixedRate} or {@link #fixedDelay} task.
* @return the initial delay in milliseconds
* @since 3.2
*/
long initialDelay() default -1;
/**
* Number of milliseconds to delay before the first execution of a
* {@link #fixedRate} or {@link #fixedDelay} task.
* @return the initial delay in milliseconds as a String value, e.g. a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
*/
String initialDelayString() default "";
}
复制代码
需要执行定时任务的方法加注解@Scheduled,该注解的入口是在ScheduledAnnotationBeanPostProcessor.java里面,怎么找到可查看Spring boot的启动流程分析。
测试代码
@Component
@Slf4j
public class MyTask {
private List<Integer> index = Arrays.asList(8 * 1000, 3 * 1000, 6 * 1000, 2 * 1000, 2 * 1000);
private AtomicInteger atomicInteger = new AtomicInteger(0);
// @Scheduled(/*initialDelay = 1000, */fixedRate = 5 * 1000)
// @Scheduled(/*initialDelay = 1000,*/ fixedDelay = 5 * 1000)
@Scheduled(cron = "0/5 * * * * ? ")
public void testScheduleCron() throws InterruptedException {
int i = atomicInteger.get();
if (i < 5) {
Integer sleepTime = index.get(i);
log.info("第{}个任务开始执行,执行时间为{}ms", i, sleepTime);
Thread.sleep(sleepTime);
atomicInteger.getAndIncrement();
}
}
}
复制代码
注解带人了要执行的目前对象和方法
初始延迟间隔时间
cron表达式逻辑
fixed delay逻辑
扫描二维码关注公众号,回复:
13167607 查看本文章
![](/qrcode.jpg)
fixed rate逻辑
继续追
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
复制代码
原来是用Executors.newSingleThreadScheduledExecutor()
借肥朝的图片
cron
每隔一个周期去检查是没任务执行,没有就执行,如果有只能等下一个周期到了再检查一次。看起来有点笨,但是理解起来简单。而且cron表达式比较丰富
fixed Delay
上一个任务执行完,等一个周期后再执行
fixed Rate
这个就不好描述了,如果上一个任务执行完了,还没超过间隔周期,就等到原本按照周期的时间再开始下一个任务,这时间是从第一个任务开始算的。
如果上一个任务执行超过了间隔时间,下一个任务就立刻执行。
cron表达式实现里面用了Tigger
具体实现是 CronTrigger.java
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date date;
Date lastDate = triggerContext.lastCompletionTime();
if (lastDate != null) {
Date scheduled = triggerContext.lastScheduledExecutionTime();
//这个if else 很好理解,如果上一个完成时间早于预计执行时间,那么按照计划来
//否则立刻开始
if (scheduled != null && lastDate.before(scheduled)) {
// Previous task apparently executed too early...
// Let's simply use the last calculated execution time then,
// in order to prevent accidental re-fires in the same second.
date = scheduled;
}
}
else {
date = new Date();
}
ZonedDateTime dateTime = ZonedDateTime.ofInstant(date.toInstant(), this.zoneId);
ZonedDateTime next = this.expression.next(dateTime);
return next != null ? Date.from(next.toInstant()) : null;
}
复制代码
这里提到了定时任务管理,有trigger概念。记得定时任务管理组件,如果xxl-job、elastic-job里面也有触发器的概念,下次对比一下