• JAVA定时任务怎么实现


    序章:定时任务实现方式

    当下,java编码过程中,实现定时任务的方式主要以以下两种为主

    • spring框架的@Scheduled
    • quzrtz框架

    网络上关于这两种框架的实践和配置相关的教程很多,这里不再赘述。

    本文主要就二者的框架原理实现做一个入门引导,为了解深层实现细节做一定的铺垫。

    本文源码版本:

    • spring-context-3.2.18.RELEASE.jar
    • quartz-1.8.6.jar

    一、Scheduled

    1.1 使用方法

    1. class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@EnableScheduling // @EnableScheduling 在配置类上使用,开启计划任务的支持
    2. @Component(value="myClass")// 由spring管理
    3. public class MyClass {
    4. @Scheduled(cron= "0 0 0 * * ?")//0 0 12 * * ? 每天12点触发0 0 0/1 * * ? 0 0 0 * * ?
    5. public void myTask() {
    6. // 业务逻辑
    7. ...
    8. }
    9. }
  • 1.2 源码分析

    1.2.1 定时任务执行入口在哪?

    1. class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar
    2. public void onApplicationEvent(ContextRefreshedEvent event) {
    3. if (event.getApplicationContext() != this.applicationContext) {
    4. return;
    5. }
    6. // 定时任务执行入口方法绑定到容器生命周期上
    7. scheduleTasks();
    8. }

1.2.2 调用链路

  1. class="prettyprint hljs gradle" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">1. 所有已注册task
  2. org.springframework.scheduling.config.ScheduledTaskRegistrar
  3. protected void scheduleTasks() {
  4. ...
  5. if (this.triggerTasks != null) {
  6. for (TriggerTask task : this.triggerTasks) {
  7. // 执行初始化完成的task和Trigger
  8. this.scheduledFutures.add(this.taskScheduler.schedule(
  9. task.getRunnable(), task.getTrigger()));
  10. }
  11. }
  12. ...
  13. }
  14. 2. 单个task
  15. org.springframework.scheduling.TaskScheduler
  16. ScheduledFuture schedule(Runnable task, Trigger trigger);
  17. 3. 线程池执行task
  18. org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
  19. public ScheduledFuture schedule(Runnable task, Trigger trigger) {
  20. ScheduledExecutorService executor = getScheduledExecutor();
  21. try {
  22. ErrorHandler errorHandler =
  23. (this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));
  24. // 调用具体的实现方法.schedule()
  25. return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule();
  26. }
  27. catch (RejectedExecutionException ex) {
  28. throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
  29. }
  30. }
  31. 4. 这块是具体的线程实现细节,已经与schedul无关
  32. private ScheduledFuture schedule(final ScheduledFutureTask task) {
  33. if (task == null) {
  34. throw new NullPointerException("task");
  35. } else {
  36. if (this.inEventLoop()) {
  37. this.delayedTaskQueue.add(task);
  38. } else {
  39. // 此处就是真正的线程执行方法
  40. this.execute(new Runnable() {
  41. public void run() {
  42. SingleThreadEventExecutor.this.delayedTaskQueue.add(task);
  43. }
  44. });
  45. }
  46. return task;
  47. }
  48. }

1.2.3 @Scheduled注解的生效原理

  1. class="prettyprint hljs gradle" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor
  2. // BeanPostProcessor生命周期方法,spring加载的时候会执行
  3. public Object postProcessAfterInitialization(final Object bean, String beanName) {
  4. Class targetClass = AopUtils.getTargetClass(bean);
  5. if (!this.nonAnnotatedClasses.containsKey(targetClass)) {
  6. final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1);
  7. ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
  8. public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
  9. Scheduled scheduled = AnnotationUtils.getAnnotation(method, Scheduled.class);
  10. if (scheduled != null) {
  11. // @Scheduled的真正解析方法,具体解析细节和参数参看源码
  12. // 解析后添加到ScheduledTaskRegistrar里
  13. // 全部任务解析完成,执行ScheduledTaskRegistrar,具体实现参看[1.2.2 调用链路]章节
  14. processScheduled(scheduled, method, bean);
  15. annotatedMethods.add(method);
  16. }
  17. }
  18. });
  19. if (annotatedMethods.isEmpty()) {
  20. this.nonAnnotatedClasses.put(targetClass, Boolean.TRUE);
  21. }
  22. }
  23. return bean;
  24. }

二、QUARTZ

2.1 使用方法

  1. class="prettyprint hljs fsharp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">// 实例化一个调度器工厂,每个应用只有唯一一个工厂实例
  2. SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
  3. // 实例化一个调度器
  4. Scheduler sched = schedFact.getScheduler();
  5. // 启动,只有启动了调度器Quartz才会去执行任务
  6. sched.start();
  7. // 实例化一个任务
  8. JobDetail job = newJob(HelloJob.class)
  9. .withIdentity("myJob", "group1")
  10. .build();
  11. // 实例化一个任务触发器,立刻触发,每40s执行一次
  12. Trigger trigger = newTrigger()
  13. .withIdentity("myTrigger", "group1")
  14. .startNow()
  15. .withSchedule(simpleSchedule()
  16. .withIntervalInSeconds(40)
  17. .repeatForever())
  18. .build();
  19. // 调度任务
  20. sched.scheduleJob(job, trigger);

2.2 源码分析

2.2.1 启动入口

  1. class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">1\. web.xml配置
  2. <param-name>quartz:config-fileparam-name>
  3. <param-value>/some/path/my_quartz.propertiesparam-value>
  4. <context-param>
  5. <param-name>quartz:shutdown-on-unloadparam-name>
  6. <param-value>trueparam-value>
  7. context-param>
  8. <context-param>
  9. <param-name>quartz:start-on-loadparam-name>
  10. <param-value>trueparam-value>
  11. context-param>
  12. <listener>
  13. <listener-class>
  14. org.quartz.ee.servlet.QuartzInitializerListener
  15. listener-class>
  16. listener>
  17. 2\. org.quartz.ee.servlet.QuartzInitializerListener
  18. // 执行ServletContextListener.contextInitialized的容器生命周期方法
  19. public void contextInitialized(ServletContextEvent sce) {
  20. ...
  21. // 根据自定义的配置文件加载SchedulerFactory
  22. if (configFile != null) {
  23. factory = new StdSchedulerFactory(configFile);
  24. } else {
  25. factory = new StdSchedulerFactory();
  26. }
  27. // 加载scheduler
  28. scheduler = factory.getScheduler();
  29. // 启动scheduler
  30. scheduler.start();
  31. log.info("Scheduler has been started...");
  32. ...
  33. }

2.2.2 核心方法详解

  1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">1. StdSchedulerFactory.getScheduler()
  2. public Scheduler getScheduler() throws SchedulerException {
  3. if (cfg == null) {
  4. // 根据不同的配置方式加载对应配置
  5. initialize();
  6. }
  7. ...
  8. // 加载实例(加载Scheduler整个上下文环境)
  9. sched = instantiate();
  10. return sched;
  11. }
  12. 2. StdSchedulerFactory.getScheduler().instantiate()
  13. 具体实现代码很多,以下做伪代码描述
  14. private Scheduler instantiate() throws SchedulerException {
  15. // 校验初始化
  16. if (cfg == null) {
  17. initialize();
  18. }
  19. // 获取 Scheduler
  20. // 加载 ThreadPool
  21. // 加载 JobStore
  22. // 加载 DataSources
  23. // 加载 SchedulerPlugins
  24. // 加载 JobListeners
  25. // 加载 TriggerListeners
  26. // 加载 ThreadExecutor
  27. // 构造QuartzScheduler
  28. qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);
  29. Scheduler scheduler = instantiate(rsrcs, qs);
  30. qs.initialize();
  31. // 返回实例化好的scheduler
  32. return scheduler;
  33. }
  • 相关阅读:
    TensorFlow 的基本概念和使用场景
    【机器学习算法】序列模式的概念,Aprioriall算法和SrefixSpan算法
    actuator--基础--6.1--端点解析--health端点
    【小沐学Python】Python实现在线电子书(MkDocs + readthedocs + github + Markdown)
    kotlin 之几个常见的内联函数(二)
    【mmdetection】ROIExtractor中的featmap_strides和finest_scale
    Python学习笔记--面向对象的概念
    吃完饭后,到底是躺着、坐着、站着还是运动?看完终于不纠结了
    Python: 开始使用工厂模式设计
    BUUCTF学习(8): 随便注,SQL
  • 原文地址:https://blog.csdn.net/weixin_62421895/article/details/126053952