线程池--Spring定时任务执行原理分析

线程池相关--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();
        }
    }

}
复制代码

注解带人了要执行的目前对象和方法

image-20211013183133226

初始延迟间隔时间

image-20211013183354059

cron表达式逻辑

image-20211013183443310

fixed delay逻辑

image-20211013183518677

fixed rate逻辑

image-20211013183556204

继续追

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()

image-20211013184540653

肥朝的图片

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里面也有触发器的概念,下次对比一下

猜你喜欢

转载自juejin.im/post/7018508571767734279