JAVA定时任务原理入门

本文适用语言:java

序章:定时任务实现方式

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

  • spring框架的@Scheduled
  • quzrtz框架

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

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

本文源码版本:

  • 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.ContextLifecycleScheduledTaskRegistrarpublic void onApplicationEvent(ContextRefreshedEvent event) {if (event.getApplicationContext() != this.applicationContext) {return;}// 定时任务执行入口方法绑定到容器生命周期上scheduleTasks();}

1.2.2 调用链路

1. 所有已注册taskorg.springframework.scheduling.config.ScheduledTaskRegistrarprotected void scheduleTasks() {    ...    if (this.triggerTasks != null) {    for (TriggerTask task : this.triggerTasks) {            // 执行初始化完成的task和Trigger    this.scheduledFutures.add(this.taskScheduler.schedule(    task.getRunnable(), task.getTrigger()));    }    }    ...}2. 单个taskorg.springframework.scheduling.TaskSchedulerScheduledFuture schedule(Runnable task, Trigger trigger);3. 线程池执行taskorg.springframework.scheduling.concurrent.ThreadPoolTaskSchedulerpublic 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  ScheduledFuture schedule(final ScheduledFutureTask 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 annotatedMethods = new LinkedHashSet(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配置   quartz:config-file   /some/path/my_quartz.properties   quartz:shutdown-on-unload   true   quartz:start-on-load   true          org.quartz.ee.servlet.QuartzInitializerListener   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;}
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章