开发踩坑记录之二:谨慎使用Spring中的@Scheduled注解,并发编程面试题

本文详细解释了Spring框架如何处理带有@Schedule注解的方法,包括根据参数判断任务类型,如cron、fixedDelay和fixedRate,以及如何设置初始延迟和时区,并最终将任务注册到ScheduledTaskRegistrar中。
摘要由CSDN通过智能技术生成

return bean;

}

再往下继续看,Spring是如何处理带有@Schedule注解的方法的。processScheduled获取scheduled类参数,之后根据参数类型、相应的延时时间、对应的时区将定时任务放入不同的任务列表中。

protected void processScheduled(Scheduled scheduled, Method method, Object bean) {

try {

Assert.isTrue(method.getParameterCount() == 0,

“Only no-arg methods may be annotated with @Scheduled”);

//获取调用的方法

Method invocableMethod = AopUtils.selectInvocableMethod(method, bean.getClass());

//处理线程

Runnable runnable = new ScheduledMethodRunnable(bean, invocableMethod);

boolean processedSchedule = false;

String errorMessage =

“Exactly one of the ‘cron’, ‘fixedDelay(String)’, or ‘fixedRate(String)’ attributes is required”;

Set tasks = new LinkedHashSet<>(4);

// Determine initial delay

long initialDelay = scheduled.initialDelay();

String initialDelayString = scheduled.initialDelayString();

if (StringUtils.hasText(initialDelayString)) {

Assert.isTrue(initialDelay < 0, “Specify ‘initialDelay’ or ‘initialDelayString’, not both”);

if (this.embeddedValueResolver != null) {

initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);

}

if (StringUtils.hasLength(initialDelayString)) {

try {

initialDelay = parseDelayAsLong(initialDelayString);

}

catch (RuntimeException ex) {

throw new IllegalArgumentException(

“Invalid initialDelayString value “” + initialDelayString + “” - cannot parse into long”);

}

}

}

// 获取cron参数

String cron = scheduled.cron();

if (StringUtils.hasText(cron)) {

String zone = 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’ not supported for cron triggers”);

processedSchedule = true;

TimeZone timeZone;

if (StringUtils.hasText(zone)) {

timeZone = StringUtils.parseTimeZoneString(zone);

}

else {

timeZone = TimeZone.getDefault();

}

//加入到定时任务列表中

tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));

}

}

// At this point we don’t need to differentiate between initial delay set or not anymore

if (initialDelay < 0) {

initialDelay = 0;

}

// Check fixed delay

long fixedDelay = scheduled.fixedDelay();

if (fixedDelay >= 0) {

Assert.isTrue(!processedSchedule, errorMessage);

processedSchedule = true;

tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));

}

String fixedDelayString = scheduled.fixedDelayString();

if (StringUtils.hasText(fixedDelayString)) {

if (this.embeddedValueResolver != null) {

fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);

}

if (StringUtils.hasLength(fixedDelayString)) {

Assert.isTrue(!processedSchedule, errorMessage);

processedSchedule = true;

try {

fixedDelay = parseDelayAsLong(fixedDelayString);

}

catch (RuntimeException ex) {

throw new IllegalArgumentException(

“Invalid fixedDelayString value “” + fixedDelayString + “” - cannot parse into long”);

}

tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));

}

}

// 执行频率的类型为long

long fixedRate = scheduled.fixedRate();

if (fixedRate >= 0) {

Assert.isTrue(!processedSchedule, errorMessage);

processedSchedule = true;

tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));

}

String fixedRateString = scheduled.fixedRateString();

if (StringUtils.hasText(fixedRateString)) {

if (this.embeddedValueResolver != null) {

fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);

}

if (StringUtils.hasLength(fixedRateString)) {

Assert.isTrue(!processedSchedule, errorMessage);

processedSchedule = true;

try {

fixedRate = parseDelayAsLong(fixedRateString);

}

catch (RuntimeException ex) {

throw new IllegalArgumentException(

“Invalid fixedRateString value “” + fixedRateString + “” - cannot parse into long”);

}

tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));

}

}

// Check whether we had any attribute set

Assert.isTrue(processedSchedule, errorMessage);

// Finally register the scheduled tasks

synchronized (this.scheduledTasks) {

Set registeredTasks = this.scheduledTasks.get(bean);

if (registeredTasks == null) {

registeredTasks = new LinkedHashSet<>(4);

this.scheduledTasks.put(bean, registeredTasks);

}

registeredTasks.addAll(tasks);

}

}

catch (IllegalArgumentException ex) {

throw new IllegalStateException(

“Encountered invalid @Scheduled method '” + method.getName() + "': " + ex.getMessage());

}

}

满足条件时将定时任务添加到定时任务列表中,在加入任务列表的同时对定时任务进行注册。ScheduledTaskRegistrar这个类为Spring容器的定时任务注册中心。以下为ScheduledTaskRegistrar部分源码,主要说明该类中包含的属性。Spring容器通过线程处理注册的定时任务。

public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean {

private TaskScheduler taskScheduler;

private ScheduledExecutorService localExecutor;

private List triggerTasks;

private List cronTasks;

private List fixedRateTasks;

private List fixedDelayTasks;

private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<Task, ScheduledTask>(16);

private final Set scheduledTasks = new LinkedHashSet(16);

}

ScheduledTaskRegistrar类中在处理定时任务时会调用scheduleCronTask方法初始化定时任务。

public ScheduledTask scheduleCronTask(CronTask task) {

ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);

boolean newTask = false;

if (scheduledTask == null) {

scheduledTask = new ScheduledTask();

newTask = true;

}

if (this.taskScheduler != null) {

scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());

}

else {

addCronTask(task);

this.unresolvedTasks.put(task, scheduledTask);

}

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

1200页Java架构面试专题及答案

小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞

百度、字节、美团等大厂常见面试题

z-1711034661663)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-XLGuzMiy-1711034661663)]

1200页Java架构面试专题及答案

小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞

[外链图片转存中…(img-VNwkcwGk-1711034661664)]

[外链图片转存中…(img-L2rqbGfn-1711034661664)]

百度、字节、美团等大厂常见面试题

[外链图片转存中…(img-bcjy7aHn-1711034661665)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值