本文主要是介绍源码剖析之@Scheduled与ThreadPoolTaskScheduler,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
引言
在Java并发编程领域,@Scheduled
注解和ScheduledThreadPoolExecutor
是两个关键组件,前者在Spring框架中被用来简化定时任务的配置与执行,后者则是JDK内置的用于处理周期性和延时任务的线程池。本文将透过源码视角,详尽解析二者的工作机制及其内在联系。
一、@Scheduled
使用方式
在Spring中,我们可以使用@Schedule注解来定义一个定时任务方法。例如:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class MyTask {@Scheduled(fixedRate = 5000)public void doSomething() {System.out.println("执行任务");}
}
源码分析
虽然@Scheduled
注解并不直接涉及ScheduledThreadPoolExecutor
的源码,但它是与Spring框架中定时任务调度密切相关的。@Scheduled
注解允许我们在方法级别定义定时任务:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scheduled {// ...String cron() default "";long fixedRate() default -1L;long fixedDelay() default -1L;
}
ScheduledAnnotationBeanPostProcessor
是 Spring 框架中用于处理带有 @Scheduled
注解的任务类的核心类。它主要用于扫描和管理所有被 @Scheduled
注解标记的方法,使其按照设定的时间计划自动执行。
-
继承
ScheduledTaskHolder
:这个接口主要提供对所有已注册的计划任务的访问能力。通过实现这个接口,ScheduledAnnotationBeanPostProcessor
可以作为持有计划任务的容器,可以查询、管理这些基于注解的任务。 -
实现
ApplicationListener<E>
:Spring 中的事件监听器接口,这意味着ScheduledAnnotationBeanPostProcessor
可以监听应用上下文中的特定事件(如 ContextRefreshedEvent 等)。当接收到相关事件时,它可以进行相应的响应操作,例如在 Spring 容器初始化完成后开始执行计划任务。 -
实现
SmartInitializingSingleton
:这是 Spring 内部的一个接口,表示一个在 Spring 应用上下文完全初始化后需要执行一次性初始化逻辑的对象。对于ScheduledAnnotationBeanPostProcessor
来说,就是在 Spring 容器完成初始化之后,开始调度那些带有@Scheduled
注解的任务。
注解注入源码
public Object postProcessAfterInitialization(Object bean, String beanName) {if (!(bean instanceof AopInfrastructureBean) && !(bean instanceof TaskScheduler) && !(bean instanceof ScheduledExecutorService)) {Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);if (!this.nonAnnotatedClasses.contains(targetClass) && AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (method) -> {Set<Scheduled> scheduledAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);return !scheduledAnnotations.isEmpty() ? scheduledAnnotations : null;});if (annotatedMethods.isEmpty()) {this.nonAnnotatedClasses.add(targetClass);if (this.logger.isTraceEnabled()) {this.logger.trace("No @Scheduled annotations found on bean class: " + targetClass);}} else {annotatedMethods.forEach((method, scheduledAnnotations) -> {scheduledAnnotations.forEach((scheduled) -> {this.processScheduled(scheduled, method, bean);});});if (this.logger.isTraceEnabled()) {this.logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods);}}}return bean;} else {return bean;}}
这段代码是 Spring 框架中 ScheduledAnnotationBeanPostProcessor
类的 postProcessAfterInitialization
方法实现,它是 Spring 的一个 Bean 后处理器方法。在 Spring 容器初始化 Bean 过程中调用此方法来处理带有 @Scheduled
注解的方法。
方法的主要逻辑如下:
-
首先检查传入的
bean
是否是 AOP 基础结构相关的 Bean,或者是TaskScheduler
或ScheduledExecutorService
的实例。如果是,则直接返回原bean
,不进行后续处理。 -
如果不是上述类型,则获取 Bean 的最终目标类(即如果 Bean 是代理对象,则获取其代理的真实类),并判断该类是否包含
Scheduled
或Schedules
注解,且未被先前处理过。 -
使用
MethodIntrospector
选择出目标类中所有带有Scheduled
或Schedules
注解的方法,并将注解信息存入Map<Method, Set<Scheduled>>
结构中。 -
如果没有找到任何带有
Scheduled
注解的方法,则将目标类添加到已知不包含注解的类集合中,并记录一条日志信息。 -
如果找到了带有
Scheduled
注解的方法,则遍历这些方法及其对应的注解集合并调用processScheduled
方法处理每个注解,从而将该方法配置为一个定时任务。 -
最后,在日志级别允许的情况下,输出已处理的带有
@Scheduled
注解的方法数量以及详细信息。
二、ScheduledThreadPoolExecutor
使用方式
以下是一个简单的使用ScheduledThreadPoolExecutor执行定时任务的示例:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class MyTask {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);Runnable task = () -> System.out.println("执行任务");executor.scheduleAtFixedRate(task, 0, 5, TimeUnit.SECONDS);}
}
上述代码中,我们创建了一个包含1个线程的ScheduledThreadPoolExecutor,并使用scheduleAtFixedRate方法创建一个定时任务,每隔5秒执行一次。
源码分析
ScheduledThreadPoolExecutor
是Java并发库的核心类之一,它扩展了ThreadPoolExecutor
,并实现了ScheduledExecutorService
接口,专门用于处理定时任务。
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutorimplements ScheduledExecutorService {// ...public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {// ...}public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {// ...}public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {// ...}public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {// ...}// ...
}
ScheduledThreadPoolExecutor
主要提供了四种方法用于创建不同类型的定时任务,包括一次性延时执行、一次性定时执行以及按固定速率或固定延迟周期执行。内部实现通过维护一个优先级队列(通常是DelayedWorkQueue
)来排序待执行的任务,并利用一个后台线程不断地从队列中取出到期的任务执行。
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,它的主要特点是支持定时任务。在内部,ScheduledThreadPoolExecutor维护了一个优先队列,用于存储待执行的任务。当任务到达指定的执行时间时,它会从优先队列中取出任务并执行。
ScheduledThreadPoolExecutor提供了两种定时任务的执行策略:scheduleAtFixedRate和scheduleWithFixedDelay。前者表示任务按照固定的速率执行,后者表示任务在上一次任务完成后等待固定的延迟后执行。
三、@Scheduled与ScheduledThreadPoolExecutor的协同工作
在Spring框架中,@Scheduled
注解定义的定时任务会被转换为ScheduledThreadPoolExecutor
所能识别的形式,然后提交给ScheduledThreadPoolExecutor
执行。例如,对于@Scheduled(cron = "0 0/5 * * * ?")
这样的cron表达式定时任务,Spring会将其解析并映射到ScheduledThreadPoolExecutor
的周期性执行方法上。
总的来说,@Scheduled
注解和ScheduledThreadPoolExecutor
在Java并发编程中相辅相成,前者极大简化了定时任务的配置,后者则作为强大的底层引擎,确保了定时任务的高效稳定执行。通过深入理解它们的源码实现,开发者可以更好地掌握定时任务调度技术,优化程序性能,提升系统稳定性。
这篇关于源码剖析之@Scheduled与ThreadPoolTaskScheduler的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!