3

JAVA定时任务原理入门 - HACKXIYU

 2 years ago
source link: https://www.cnblogs.com/hackxiyu/p/16529783.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文适用语言: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.ContextLifecycleScheduledTaskRegistrar public 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 <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;}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK