Java定时任务
HACKXIYU 人气:0序章
定时任务实现方式
当下,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.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; }
加载全部内容