1. 程式人生 > >[email protected]定時任務原始碼解析

[email protected]定時任務原始碼解析

本文以springboot中cron表示式配置的定時任務為例子。

在springboot中的啟動類中新增@EnableScheduling註解,在beanFactory中新增ScheduledAnnotationBeanPostProcessor作為bean初始化完畢後的後置處理器來新增關於spring的定時任務處理支援。

在ScheduledAnnotationBeanPostProcessor類中,含有定時任務的註冊器ScheduleTaskRegistrar來負責管理定時任務。

private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();

在定時任務處理器在beanFactory中作為bean建立完畢之後,將會呼叫finishRegistration()方法來完成相關注冊器的一系列配置。
public void onApplicationEvent(ContextRefreshedEvent event) {
   if (event.getApplicationContext() == this.applicationContext) {
            finishRegistration();
   }
}

private void finishRegistration() {
   if (this.scheduler != null) {
      this.registrar.setScheduler(this.scheduler);
   }

   if (this.beanFactory instanceof ListableBeanFactory) {
      Map<String, SchedulingConfigurer> configurers =
            ((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
      for (SchedulingConfigurer configurer : configurers.values()) {
         configurer.configureTasks(this.registrar);
      }
   }

   if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
      Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
      try { 
         this.registrar.setTaskScheduler(this.beanFactory.getBean(TaskScheduler.class));
      }
      catch (NoUniqueBeanDefinitionException ex) {
         try {
            this.registrar.setTaskScheduler(
                  this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, TaskScheduler.class));
         }
         catch (NoSuchBeanDefinitionException ex2) {
            if (logger.isInfoEnabled()) {
               logger.info("More than one TaskScheduler bean exists within the context, and " +
                     "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
                     "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
                     "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
                     ex.getBeanNamesFound());
            }
         }
      }
      catch (NoSuchBeanDefinitionException ex) {
         logger.debug("Could not find default TaskScheduler bean", ex);
         try {
            this.registrar.setScheduler(this.beanFactory.getBean(ScheduledExecutorService.class));
         }
         catch (NoUniqueBeanDefinitionException ex2) {
            try {
               this.registrar.setScheduler(
                     this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, ScheduledExecutorService.class));
            }
            catch (NoSuchBeanDefinitionException ex3) {
               if (logger.isInfoEnabled()) {
                  logger.info("More than one ScheduledExecutorService bean exists within the context, and " +
                        "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
                        "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
                        "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
                        ex2.getBeanNamesFound());
               }
            }
         }
         catch (NoSuchBeanDefinitionException ex2) {
            logger.debug("Could not find default ScheduledExecutorService bean", ex2); 
            logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
         }
      }
   }

   this.registrar.afterPropertiesSet();
}
在此處,如果已經在註冊器當中已經註冊有相關的任務,但是註冊器當中的定時工作管理員如果不存在,將會試著從beanFactory將工作管理員設定相應的管理器。在這之後,將會呼叫註冊器的afterPropertiesSet()方法,來進行註冊器的相關配置。

public void afterPropertiesSet() {
   scheduleTasks();
}
protected void scheduleTasks() {
   if (this.taskScheduler == null) {
      this.localExecutor = Executors.newSingleThreadScheduledExecutor();
      this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
   }
   if (this.triggerTasks != null) {
      for (TriggerTask task : this.triggerTasks) {
         addScheduledTask(scheduleTriggerTask(task));
      }
   }
   if (this.cronTasks != null) {
      for (CronTask task : this.cronTasks) {
         addScheduledTask(scheduleCronTask(task));
      }
   }
   if (this.fixedRateTasks != null) {
      for (IntervalTask task : this.fixedRateTasks) {
         addScheduledTask(scheduleFixedRateTask(task));
      }
   }
   if (this.fixedDelayTasks != null) {
      for (IntervalTask task : this.fixedDelayTasks) {
         addScheduledTask(scheduleFixedDelayTask(task));
      }
   }
}

在afterPropertiesSet方法中直接呼叫scheduleTask()方法進行相關注冊器配置。

在這裡,我們可以看一下注冊器的成員。

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<Task, ScheduledTask>(16);

成員相當直觀。

其中四個list分別儲存還沒有加進執行緒池四種方式配置的定時任務任務,在不存在管理器和執行緒池的情況下快取在其中,一旦建立起兩者,現試圖將這四個list儲存的任務加進管理器和執行緒池。本文主要解析cron表示式配置的定時任務。

unresolvedTasks主要儲存還沒有正式進入執行緒池的定時任務配置類與該配置類具體的定時任務例項的鍵值對。

taskSceduler作為註冊器當中的工作管理員,localExcutor作為註冊器的執行緒池。可以剖析一下兩者的關係。

先看執行緒池ScheduledExecutorService類。

ScheduledExecutorService作為介面直接繼承了jdk的執行緒池ExecutorService介面。可見,spring的定時任務執行緒池是完全基於jdk的執行緒池的實現的。而我們可以在scheduleTasks()方法裡看到預設採用的執行緒池的實現。Executors.newSingleThreadScheduledExecutor()方法直接申請了執行緒池(Executors是jdk給出的直接申請執行緒池的工具類)。

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
          new DelayedWorkQueue());
}

可以讓人欣喜的發現,ScheduledThreadPoolExecutor作為執行緒池直接繼承自ThreadPoolExecutor執行緒池,主要來支援週期性方法的排程,即使是在構造方法,也是直接super()了ThreadPoolExecutor的構造方法。 值得注意的是,corePoolSize為1,這代表著這個執行緒池的工作執行緒在一般情況下只有一條。

我們可以看相比父類,ScheduledThreadPoolExecutor最核心的schedule()方法。

public ScheduledFuture<?> schedule(Runnable command,
                                   long delay,
                                   TimeUnit unit) {
    if (command == null || unit == null)
        throw new NullPointerException();
    RunnableScheduledFuture<?> t = decorateTask(command,
        new ScheduledFutureTask<Void>(command, null,
                                      triggerTime(delay, unit)));
    delayedExecute(t);
    return t;
}

這一方法需要三個引數,分別是所要執行執行的具體任務,後兩者分別是每兩次執行相隔的時間與時間單位。但是,spring中的定時執行週期任務並不是依靠這裡來實現的。這裡我們將時間和任務作為引數來生成內部任務類,ScheduledFutureTask類(繼承自FutureTask類),但是我們可以看相關的構造方法。

ScheduledFutureTask(Runnable r, V result, long ns) {
    super(r, result);
    this.time = ns;
    this.period = 0;
    this.sequenceNumber = sequencer.getAndIncrement();
}

相關週期執行的引數:

private final long period;

0代表該任務不是週期執行的方法。

public void run() {
    boolean periodic = isPeriodic();
    if (!canRunInCurrentRunState(periodic))
        cancel(false);
    else if (!periodic)
        ScheduledFutureTask.super.run();
    else if (ScheduledFutureTask.super.runAndReset()) {
        setNextRunTime();
        reExecutePeriodic(outerTask);
    }
}

在ScheduledFutureTask類中,如果period如果為0,只是簡單呼叫父類(FutureTask)的run方法來執行所需要的任務,並沒有週期執行。

線上程池schedule()方法的最後,呼叫delayedExecute()方法加入延時佇列以確保任務的準時進行。

以上,是spring定時任務註冊器當中的定時任務執行緒池。

接下來是定時任務註冊器的定時工作管理員。

定時工作管理員在scheduleTasks()方法,預設ConcurrentTaskScheduler類來實現。

可以明顯看到直接在ConcurrentTaskScheduler的構造方法中將現有的定時任務執行緒池傳入作為管理器的執行緒池。具體使用,接下里再說。

在scheduleTasks()方法的最後,將現有的未執行的定時任務放入剛剛初始化完畢的管理器執行任務。

以上是定時任務後置處理器與註冊器初始化的具體流程。

下面,具體的定時任務裝載與執行。

public Object postProcessAfterInitialization(final Object bean, String beanName) {
   Class<?> targetClass = AopUtils.getTargetClass(bean);
   if (!this.nonAnnotatedClasses.contains(targetClass)) {
      Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
            new MethodIntrospector.MetadataLookup<Set<Scheduled>>() {
               @Override
               public Set<Scheduled> inspect(Method method) {
                  Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                        method, Scheduled.class, Schedules.class);
                  return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
               }
            });
      if (annotatedMethods.isEmpty()) {
         this.nonAnnotatedClasses.add(targetClass);
         if (logger.isTraceEnabled()) {
            logger.trace("No @Scheduled annotations found on bean class: " + bean.getClass());
         }
      }
      else {
         for (Map.Entry<Method, Set<Scheduled>> entry : annotatedMethods.entrySet()) {
            Method method = entry.getKey();
            for (Scheduled scheduled : entry.getValue()) {
               processScheduled(scheduled, method, bean);
            }
         }
         if (logger.isDebugEnabled()) {
            logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
                  "': " + annotatedMethods);
         }
      }
   }
   return bean;
}

在beanFacctory中bean裝載完畢後,呼叫後置處理器的postProcessAfterInitialization()方法。

首先,遍歷bean的方法,找到所有經過@scheduled註解的方法。如果有,說明該bean有需要被執行的定時任務。那麼,遍歷bean中所有被註解的方法,依次呼叫processScheduled()方法完成定時任務的裝載註冊與執行。


protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
   try {
      Assert.isTrue(method.getParameterTypes().length == 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<ScheduledTask> tasks = new LinkedHashSet<ScheduledTask>(4);

      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);
         }
         try {
            initialDelay = Long.parseLong(initialDelayString);
         }
         catch (NumberFormatException ex) {
            throw new IllegalArgumentException(
                  "Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");
         }
      }

      String cron = scheduled.cron();
      if (StringUtils.hasText(cron)) {
         Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
         processedSchedule = true;
         String zone = scheduled.zone();
         if (this.embeddedValueResolver != null) {
            cron = this.embeddedValueResolver.resolveStringValue(cron);
            zone = this.embeddedValueResolver.resolveStringValue(zone);
         }
         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))));
      }

      if (initialDelay < 0) {
         initialDelay = 0;
      }

      long fixedDelay = scheduled.fixedDelay();
      if (fixedDelay >= 0) {
         Assert.isTrue(!processedSchedule, errorMessage);
         processedSchedule = true;
         tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
      }
      String fixedDelayString = scheduled.fixedDelayString();
      if (StringUtils.hasText(fixedDelayString)) {
         Assert.isTrue(!processedSchedule, errorMessage);
         processedSchedule = true;
         if (this.embeddedValueResolver != null) {
            fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
         }
         try {
            fixedDelay = Long.parseLong(fixedDelayString);
         }
         catch (NumberFormatException ex) {
            throw new IllegalArgumentException(
                  "Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");
         }
         tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
      }

      long fixedRate = scheduled.fixedRate();
      if (fixedRate >= 0) {
         Assert.isTrue(!processedSchedule, errorMessage);
         processedSchedule = true;
         tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
      }
      String fixedRateString = scheduled.fixedRateString();
      if (StringUtils.hasText(fixedRateString)) {
         Assert.isTrue(!processedSchedule, errorMessage);
         processedSchedule = true;
         if (this.embeddedValueResolver != null) {
            fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
         }
         try {
            fixedRate = Long.parseLong(fixedRateString);
         }
         catch (NumberFormatException ex) {
            throw new IllegalArgumentException(
                  "Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");
         }
         tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
      }

      Assert.isTrue(processedSchedule, errorMessage);

      synchronized (this.scheduledTasks) {
         Set<ScheduledTask> registeredTasks = this.scheduledTasks.get(bean);
         if (registeredTasks == null) {
            registeredTasks = new LinkedHashSet<ScheduledTask>(4);
            this.scheduledTasks.put(bean, registeredTasks);
         }
         registeredTasks.addAll(tasks);
      }
   }
   catch (IllegalArgumentException ex) {
      throw new IllegalStateException(
            "Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
   }
}


首先通過反射機制找到具體要執行的方法(確保該方法是具體可以訪問的)。

接下來則是具體的引數解析,邏輯很清楚。我們可以很簡單的發現具體配置方式的解析,我們可以直接看這段cron表示式的解析。

String cron = scheduled.cron();
if (StringUtils.hasText(cron)) {
   Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
   processedSchedule = true;
   String zone = scheduled.zone();
   if (this.embeddedValueResolver != null) {
      cron = this.embeddedValueResolver.resolveStringValue(cron);
      zone = this.embeddedValueResolver.resolveStringValue(zone);
   }
   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))));
}

得到註解中的cron表示式,以及時區資訊(如果沒有則採用預設時區)。如果存在資料直譯器,則通過資料直譯器得到具體的表示式。在獲取成功之後,首先根據cron表示式建立cron觸發器cronTrigger

private final CronSequenceGenerator sequenceGenerator;

觸發器只有一個成員,是具體的cron表示式解析的工具與存放結果,以及時間操作。

public CronTrigger(String expression, TimeZone timeZone) {
   this.sequenceGenerator = new CronSequenceGenerator(expression, timeZone);
}

在構造方法中根據cron表示式與時區,解析並存放結果。

下面是CronmSequenceGenerator的成員。

private final String expression;

private final TimeZone timeZone;

private final BitSet months = new BitSet(12);

private final BitSet daysOfMonth = new BitSet(31);

private final BitSet daysOfWeek = new BitSet(7);

private final BitSet hours = new BitSet(24);

private final BitSet minutes = new BitSet(60);

private final BitSet seconds = new BitSet(60);

太直觀了,從上往下,表示式,時區,月,月的某一天,周的某一天,時分秒。

在構造方法根據時區與cron表示式解析。

public CronSequenceGenerator(String expression, TimeZone timeZone) {
   this.expression = expression;
   this.timeZone = timeZone;
   parse(expression);
}
private void parse(String expression) throws IllegalArgumentException {
   String[] fields = StringUtils.tokenizeToStringArray(expression, " ");
   if (!areValidCronFields(fields)) {
      throw new IllegalArgumentException(String.format(
            "Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));
   }
   setNumberHits(this.seconds, fields[0], 0, 60);
   setNumberHits(this.minutes, fields[1], 0, 60);
   setNumberHits(this.hours, fields[2], 0, 24);
   setDaysOfMonth(this.daysOfMonth, fields[3]);
   setMonths(this.months, fields[4]);
   setDays(this.daysOfWeek, replaceOrdinals(fields[5], "SUN,MON,TUE,WED,THU,FRI,SAT"), 8);
   if (this.daysOfWeek.get(7)) {
      this.daysOfWeek.set(0);
      this.daysOfWeek.clear(7);
   }
}

只要瞭解了cron的表示式的具體構造,解析方式相當直觀而且簡單。

在觸發器中有直接返回下一次執行時間的方法。

public Date nextExecutionTime(TriggerContext triggerContext) {
   Date date = triggerContext.lastCompletionTime();
   if (date != null) {
      Date scheduled = triggerContext.lastScheduledExecutionTime();
      if (scheduled != null && date.before(scheduled)) {
         date = scheduled;
      }
   }
   else {
      date = new Date();
   }
   return this.sequenceGenerator.next(date);
}

很簡單,根據傳入的時間或者當前時間,直接返回下一次的呼叫時間。

具體下一次的呼叫時間通過CronmSequenceGenerator的next()方法來返回。

public Date next(Date date) {
   
   Calendar calendar = new GregorianCalendar();
   calendar.setTimeZone(this.timeZone);
   calendar.setTime(date);

   calendar.set(Calendar.MILLISECOND, 0);
   long originalTimestamp = calendar.getTimeInMillis();
   doNext(calendar, calendar.get(Calendar.YEAR));

   if (calendar.getTimeInMillis() == originalTimestamp) {
      
      calendar.add(Calendar.SECOND, 1);
      doNext(calendar, calendar.get(Calendar.YEAR));
   }

   return calendar.getTime();
}
private void doNext(Calendar calendar, int dot) {
   List<Integer> resets = new ArrayList<Integer>();

   int second = calendar.get(Calendar.SECOND);
   List<Integer> emptyList = Collections.emptyList();
   int updateSecond = findNext(this.seconds, second, calendar, Calendar.SECOND, Calendar.MINUTE, emptyList);
   if (second == updateSecond) {
      resets.add(Calendar.SECOND);
   }

   int minute = calendar.get(Calendar.MINUTE);
   int updateMinute = findNext(this.minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets);
   if (minute == updateMinute) {
      resets.add(Calendar.MINUTE);
   }
   else {
      doNext(calendar, dot);
   }

   int hour = calendar.get(Calendar.HOUR_OF_DAY);
   int updateHour = findNext(this.hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets);
   if (hour == updateHour) {
      resets.add(Calendar.HOUR_OF_DAY);
   }
   else {
      doNext(calendar, dot);
   }

   int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
   int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
   int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, resets);
   if (dayOfMonth == updateDayOfMonth) {
      resets.add(Calendar.DAY_OF_MONTH);
   }
   else {
      doNext(calendar, dot);
   }

   int month = calendar.get(Calendar.MONTH);
   int updateMonth = findNext(this.months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets);
   if (month != updateMonth) {
      if (calendar.get(Calendar.YEAR) - dot > 4) {
         throw new IllegalArgumentException("Invalid cron expression \"" + this.expression +
               "\" led to runaway search for next trigger");
      }
      doNext(calendar, dot);
   }

}

具體的cron表示式的處理這裡就暫且不展開了。

在生成完cron觸發器之後,生成cronTask,cron任務。CronTask繼承自TriggerTask,在後者的基礎上除了觸發器和具體的執行緒任務,添加了表示式的存放。

在cronTask建立完畢後,通過註冊器呼叫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);
   }
   return (newTask ? scheduledTask : null);
}

首先判斷是不是已經註冊過,如果註冊過就不用從新建立具體的scheduledTask任務了。

之後如果已經存線上程池直接呼叫註冊器的工作管理員的schedule方法將抽象的任務變成具體的定時任務,否則放入等待陣列並建立抽象任務與具體任務的鍵值對。


public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
   try {
      if (this.enterpriseConcurrentScheduler) {
         return new EnterpriseConcurrentTriggerScheduler().schedule(decorateTask(task, true), trigger);
      }
      else {
         ErrorHandler errorHandler = (this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));
         return new ReschedulingRunnable(task, trigger, this.scheduledExecutor, errorHandler).schedule();
      }
   }
   catch (RejectedExecutionException ex) {
      throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
   }
}


這裡的scheduledExecutor就是註冊器在構造方法傳入的執行緒池。這裡將會生成一個新的ReschedulingRunnable定時任務返回給註冊器。

ReschedulingRunnable繼承自DelegatingErrorHandlingRunnable類。

DelegatingErrorHandlingRunnable實現了Runnable的介面,自然就有run()方法。兩個成員。

private final Runnable delegate;

private final ErrorHandler errorHandler;

所要執行的執行緒和錯誤處理器。

public void run() {
   try {
      this.delegate.run();
   }
   catch (UndeclaredThrowableException ex) {
      this.errorHandler.handleError(ex.getUndeclaredThrowable());
   }
   catch (Throwable ex) {
      this.errorHandler.handleError(ex);
   }
}

run()方法一目瞭然。

那麼就是定時任務的核心所在了。


public ReschedulingRunnable(Runnable delegate, Trigger trigger, ScheduledExecutorService executor, ErrorHandler errorHandler) {
   super(delegate, errorHandler);
   this.trigger = trigger;
   this.executor = executor;
}

ReschedulingRunnable的狗仔方法一目瞭然。在接下來管理器會呼叫ReschedulingRunnable的schedule()方法。


public ScheduledFuture<?> schedule() {
   synchronized (this.triggerContextMonitor) {
      this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
      if (this.scheduledExecutionTime == null) {
         return null;
      }
      long initialDelay = this.scheduledExecutionTime.getTime() - System.currentTimeMillis();
      this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
      return this;
   }
}

直接通過觸發器得到下次的執行時間,計算當前時間距離下次呼叫時間的具體數字,呼叫我們一開始就提到過的執行緒池的schedule方法來將該執行緒丟入執行緒池。

既然我們將其丟入了執行緒池,那一定會執行run()方法。

public void run() {
   Date actualExecutionTime = new Date();
   super.run();
   Date completionTime = new Date();
   synchronized (this.triggerContextMonitor) {
      this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
      if (!this.currentFuture.isCancelled()) {
         schedule();
      }
   }
}

在run()方法中呼叫了父類的run()方法來執行具體所要執行的任務。之後更新起止時間,如果當前任務沒有被取消,就再一次呼叫schedule()方法重複上一次操作,繼續把自己試著扔進執行緒池,已完成定期週期執行任務的目的!

在完成了上述步驟後,根據是否是第一次任務返回具體的任務或者是null。

在後置處理器依次完成了其他方式配置的任務後,將所有完成新註冊的任務存放在map中,定時任務的建立宣告完畢。