JAVA定时任务原理入门

本文适用语言:java

序章:定时任务实现方式

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

网络上关于这两种框架的实践和配置相关的教程很多,这里不再赘述。
本文主要就二者的框架原理实现做一个入门引导,为了解深层实现细节做一定的铺垫。
本文源码版本

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

一、Scheduled

1.1 使用方法

@EnableScheduling // @EnableScheduling 在配置类上使用,开启计划任务的支持 @Component(value="myClass")// 由spring管理 public class MyClass {      @Scheduled(cron= "0 0 0 * * ?")//0 0 12 * * ? 每天12点触发0 0 0/1 * * ?  0 0 0 * * ?     public void myTask() {         // 业务逻辑         ...     } } 

1.2 源码分析

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

org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar  public void onApplicationEvent(ContextRefreshedEvent event) {  if (event.getApplicationContext() != this.applicationContext) {   return;  }  // 定时任务执行入口方法绑定到容器生命周期上  scheduleTasks(); } 

1.2.2 调用链路

1. 所有已注册task org.springframework.scheduling.config.ScheduledTaskRegistrar protected void scheduleTasks() {     ...     if (this.triggerTasks != null) {      for (TriggerTask task : this.triggerTasks) {             // 执行初始化完成的task和Trigger       this.scheduledFutures.add(this.taskScheduler.schedule(         task.getRunnable(), task.getTrigger()));      }     }     ... }  2. 单个task org.springframework.scheduling.TaskScheduler ScheduledFuture schedule(Runnable task, Trigger trigger);  3. 线程池执行task org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler public ScheduledFuture schedule(Runnable task, Trigger trigger) {  ScheduledExecutorService executor = getScheduledExecutor();  try {   ErrorHandler errorHandler =     (this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));   // 调用具体的实现方法.schedule()   return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule();  }  catch (RejectedExecutionException ex) {   throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);  } }  4. 这块是具体的线程实现细节,已经与schedul无关 private <V> ScheduledFuture<V> schedule(final ScheduledFutureTask<V> task) {     if (task == null) {         throw new NullPointerException("task");     } else {         if (this.inEventLoop()) {             this.delayedTaskQueue.add(task);         } else {             // 此处就是真正的线程执行方法             this.execute(new Runnable() {                 public void run() {                     SingleThreadEventExecutor.this.delayedTaskQueue.add(task);                 }             });         }          return task;     } } 

1.2.3 @Scheduled注解的生效原理

org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor  // BeanPostProcessor生命周期方法,spring加载的时候会执行 public Object postProcessAfterInitialization(final Object bean, String beanName) {   Class<?> targetClass = AopUtils.getTargetClass(bean);  if (!this.nonAnnotatedClasses.containsKey(targetClass)) {   final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1);   ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {     Scheduled scheduled = AnnotationUtils.getAnnotation(method, Scheduled.class);     if (scheduled != null) {         // @Scheduled的真正解析方法,具体解析细节和参数参看源码         // 解析后添加到ScheduledTaskRegistrar里         // 全部任务解析完成,执行ScheduledTaskRegistrar,具体实现参看[1.2.2 调用链路]章节      processScheduled(scheduled, method, bean);      annotatedMethods.add(method);     }    }   });   if (annotatedMethods.isEmpty()) {    this.nonAnnotatedClasses.put(targetClass, Boolean.TRUE);   }  }  return bean; } 

二、QUARTZ

2.1 使用方法

// 实例化一个调度器工厂,每个应用只有唯一一个工厂实例 SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); // 实例化一个调度器 Scheduler sched = schedFact.getScheduler(); // 启动,只有启动了调度器Quartz才会去执行任务 sched.start();  // 实例化一个任务 JobDetail job = newJob(HelloJob.class)   .withIdentity("myJob", "group1")   .build();  // 实例化一个任务触发器,立刻触发,每40s执行一次 Trigger trigger = newTrigger()   .withIdentity("myTrigger", "group1")   .startNow()   .withSchedule(simpleSchedule()       .withIntervalInSeconds(40)       .repeatForever())   .build();  // 调度任务 sched.scheduleJob(job, trigger); 

2.2 源码分析

2.2.1 启动入口

 1. web.xml配置 <context-param>    <param-name>quartz:config-file</param-name>    <param-value>/some/path/my_quartz.properties</param-value> </context-param> <context-param>    <param-name>quartz:shutdown-on-unload</param-name>    <param-value>true</param-value> </context-param> <context-param>    <param-name>quartz:start-on-load</param-name>    <param-value>true</param-value> </context-param>  <listener>    <listener-class>        org.quartz.ee.servlet.QuartzInitializerListener    </listener-class> </listener>  2. org.quartz.ee.servlet.QuartzInitializerListener // 执行ServletContextListener.contextInitialized的容器生命周期方法 public void contextInitialized(ServletContextEvent sce) {     ...     // 根据自定义的配置文件加载SchedulerFactory     if (configFile != null) {         factory = new StdSchedulerFactory(configFile);     } else {         factory = new StdSchedulerFactory();     }          // 加载scheduler     scheduler = factory.getScheduler();          // 启动scheduler     scheduler.start();     log.info("Scheduler has been started...");     ... }    

2.2.2 核心方法详解

1. StdSchedulerFactory.getScheduler() public Scheduler getScheduler() throws SchedulerException {     if (cfg == null) {         // 根据不同的配置方式加载对应配置         initialize();     }     ...      // 加载实例(加载Scheduler整个上下文环境)     sched = instantiate();     return sched; }  2. StdSchedulerFactory.getScheduler().instantiate() 具体实现代码很多,以下做伪代码描述 private Scheduler instantiate() throws SchedulerException {      // 校验初始化     if (cfg == null) {         initialize();     }          // 获取 Scheduler     // 加载 ThreadPool     // 加载 JobStore     // 加载 DataSources     // 加载 SchedulerPlugins     // 加载 JobListeners     // 加载 TriggerListeners     // 加载 ThreadExecutor          // 构造QuartzScheduler     qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);     Scheduler scheduler = instantiate(rsrcs, qs);     qs.initialize();          // 返回实例化好的scheduler     return scheduler; } 

商匡云商
Logo
注册新帐户
对比商品
  • 合计 (0)
对比
0
购物车