Spring @Scheduled线程模型探究 - 源码追踪

上一篇我们来分析一下 @Scheduled 这个定时器的注解实现

首先我看下@Scheduled注解的定义

package org.springframework.scheduling.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
	String CRON_DISABLED = "-";
	String cron() default "";
	String zone() default "";
	long fixedDelay() default -1;
	String fixedDelayString() default "";
	long fixedRate() default -1;
	String fixedRateString() default "";
	long initialDelay() default -1;
	String initialDelayString() default "";
}

注解的字段分别对cron表达式、fixedDelay、fixRate进行了支持,可以传入value

紧接着我们看下@Scheduled在哪里被用到,跟进去查看一下实现
在这里插入图片描述

这里我们主要看ScheduledAnnotationBeanPostProcessor这个类,其他几个类我们稍后再说。
在这里插入图片描述

ScheduledAnnotationBeanPostProcessor是spring BeanPostProcessor的实现,postProcessAfterInitialization(Object bean, String beanName)方法在Bean初始化之后执行。

  1. 提取定时方法:抽取@Scheduled注解以及被注解的方法
  2. 处理定时方法:对定时方法进行注册委托给定时器,通过processScheduled()方法实现

processScheduled方法提取了注解上的值,cron、fixedDelay、fixRate分别包装成不同的对象,通过registrar.scheduleCronTask方法注册到registrar中

protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
		 	Runnable runnable = createRunnable(bean, method);
			Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
			
			// 确定定时器初始延迟时间
			long initialDelay = scheduled.initialDelay();
			String initialDelayString = scheduled.initialDelayString();
			...
			initialDelay = parseDelayAsLong(initialDelayString);
			...

			// cron表达式实现
			String cron = scheduled.cron();
			...
			tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
			...

			// fixed delay实现
			long fixedDelay = scheduled.fixedDelay();
			...
			tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
			...

			// fixed rate实现
			long fixedRate = scheduled.fixedRate();
			...
			tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
			...
		}
	}

registrar是ScheduledTaskRegistrar类的实例,ScheduledTaskRegistrar类主要结构如下

public class ScheduledTaskRegistrar implements ScheduledTaskHolder, InitializingBean, DisposableBean {
	private TaskScheduler taskScheduler;
	private ScheduledExecutorService localExecutor;
	private List<TriggerTask> triggerTasks;
	private List<CronTask> cronTasks;
	private List<IntervalTask> fixedRateTasks;
	private List<IntervalTask> fixedDelayTasks;
	private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<>(16);
	private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet<>(16);
	
	public void setTaskScheduler(TaskScheduler taskScheduler){...}
	public void setScheduler(@Nullable Object scheduler) {...}
	
	public ScheduledTask scheduleCronTask(CronTask task) {...}
	public ScheduledTask scheduleFixedRateTask(FixedRateTask task) {...}
	public ScheduledTask scheduleFixedDelayTask(IntervalTask task) {...}
}

以cron为例注册逻辑实现如下,把包装的Runnable方法和Trigger交由taskScheduler处理
在这里插入图片描述

再看下TaskScheduler这里是个接口,分别提供了三种模式的定义如下

public interface TaskScheduler {
	// ****** cron支持  ******
	ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
	ScheduledFuture<?> schedule(Runnable task, Date startTime);

	// ****** FixedRate支持  ******
	ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
	ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
	
	// ****** FixedDelay支持  ******
	ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
	ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);

}

看到这里我们就大概明白了,上面分析可以总结为 提取定时方法委托给taskScheduler执行。这个***taskScheduler的实现才是线程模型的关键***。又上边ScheduledTaskRegistrar类我们可以看到taskScheduler是通过setTaskScheduler、setScheduler方法传入的,我们只需要找到这俩方法在上面地方被调用即可。
在这里插入图片描述在这里插入图片描述

这俩方法都是在ScheduledAnnotationBeanPostProcessor#finishRegistration处被调用,finishRegistration在onApplicationEvent()阶段被执行
在这里插入图片描述
具体执行逻辑如下,注意两处判断逻辑,如果ScheduledAnnotationBeanPostProcessor实例的scheduler不为空就把它设置给ScheduledTaskRegistrar,否则进入下面加载逻。
在这里插入图片描述

按照先类型、再名称顺序从容器中分别查找TaskScheduler、ScheduledExecutorService对象
在这里插入图片描述

上述逻辑打断点发现默认通过this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false))方法加载到TaskScheduler实例,通过resolveNameBean得到一个NameBeanHolder实例,我们知道Spring 默认类别名为类名首字母小写,这里传入类型为TaskScheduler,holder.gerBeanName()为taskScheduler
在这里插入图片描述
TaskScheduler的实现有如下三种
在这里插入图片描述
通过debug发现加载的实例为ThreadPoolTaskScheduler类型
在这里插入图片描述
到这里我们已经知道了***@Scheduled是如何被装载处理的*** 、 委托执行对象(定时器执行单元)是如何加载的以及***加载的对象是ThreadPoolTaskScheduler类型***,那为什么默认是这个类型呢?别忘了我们还有一个非常重要的注解@EnableScheduling,这个注解是定时任务的开关、并且会进行缺省配置。TaskSchedulingAutoConfiguration为自动配置类

从配置文件中加载默认配置构造TaskSchedulerBuilder对象并执行构建方法builder进行构建
在这里插入图片描述

build()实现
在这里插入图片描述
ThreadPoolTaskScheduler默认线程为1,内部依赖JDK ScheduledExecutorService实现在这里插入图片描述 在这里插入图片描述
createExecutor()实现,可以看到创建了一个ScheduledExecutorService,默认poolSize为1
在这里插入图片描述

由此分析与验证结果一致。@Scheduled注解默认采用单线程执行,执行单元是Spring 实现的ThreadPoolTaskScheduler封装了JDK JUC调度器ScheduledExecutorService实现。

最后再往回翻代码发现,ScheduledAnnotationBeanPostProcessor#setScheduler(Object)方法的注释已经说的很清楚,如果不进行配置,默认实现为单线程。
在这里插入图片描述

接下来就是解决问题了,请见下篇《Spring @Scheduled线程模型探究 - 解决问题》。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值