Java定時任務(wù)原理詳解
序章
定時任務(wù)實現(xiàn)方式
當下,java編碼過程中,實現(xiàn)定時任務(wù)的方式主要以以下兩種為主
- spring框架的@Scheduled
- quzrtz框架
網(wǎng)絡(luò)上關(guān)于這兩種框架的實踐和配置相關(guān)的教程很多,這里不再贅述。
本文主要就二者的框架原理實現(xiàn)做一個入門引導(dǎo),為了解深層實現(xiàn)細節(jié)做一定的鋪墊。
本文源碼版本
spring-context-3.2.18.RELEASE.jar
quartz-1.8.6.jar
一、Scheduled
1.1 使用方法
@EnableScheduling // @EnableScheduling 在配置類上使用,開啟計劃任務(wù)的支持 @Component(value="myClass")// 由spring管理 public class MyClass { @Scheduled(cron= "0 0 0 * * ?")//0 0 12 * * ? 每天12點觸發(fā)0 0 0/1 * * ? 0 0 0 * * ? public void myTask() { // 業(yè)務(wù)邏輯 ... } }
1.2 源碼分析
1.2.1 定時任務(wù)執(zhí)行入口在哪?
org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext() != this.applicationContext) { return; } // 定時任務(wù)執(zhí)行入口方法綁定到容器生命周期上 scheduleTasks(); }
1.2.2 調(diào)用鏈路
1. 所有已注冊task
org.springframework.scheduling.config.ScheduledTaskRegistrar protected void scheduleTasks() { ... if (this.triggerTasks != null) { for (TriggerTask task : this.triggerTasks) { // 執(zhí)行初始化完成的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. 線程池執(zhí)行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)); // 調(diào)用具體的實現(xiàn)方法.schedule() return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule(); } catch (RejectedExecutionException ex) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); } }
4. 這塊是具體的線程實現(xiàn)細節(jié),已經(jīng)與schedul無關(guān)
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 { // 此處就是真正的線程執(zhí)行方法 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加載的時候會執(zhí)行 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的真正解析方法,具體解析細節(jié)和參數(shù)參看源碼 // 解析后添加到ScheduledTaskRegistrar里 // 全部任務(wù)解析完成,執(zhí)行ScheduledTaskRegistrar,具體實現(xiàn)參看[1.2.2 調(diào)用鏈路]章節(jié) processScheduled(scheduled, method, bean); annotatedMethods.add(method); } } }); if (annotatedMethods.isEmpty()) { this.nonAnnotatedClasses.put(targetClass, Boolean.TRUE); } } return bean; }
二、QUARTZ
2.1 使用方法
// 實例化一個調(diào)度器工廠,每個應(yīng)用只有唯一一個工廠實例 SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); // 實例化一個調(diào)度器 Scheduler sched = schedFact.getScheduler(); // 啟動,只有啟動了調(diào)度器Quartz才會去執(zhí)行任務(wù) sched.start(); // 實例化一個任務(wù) JobDetail job = newJob(HelloJob.class) .withIdentity("myJob", "group1") .build(); // 實例化一個任務(wù)觸發(fā)器,立刻觸發(fā),每40s執(zhí)行一次 Trigger trigger = newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(40) .repeatForever()) .build(); // 調(diào)度任務(wù) 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
// 執(zhí)行ServletContextListener.contextInitialized的容器生命周期方法 public void contextInitialized(ServletContextEvent sce) { ... // 根據(jù)自定義的配置文件加載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) { // 根據(jù)不同的配置方式加載對應(yīng)配置 initialize(); } ... // 加載實例(加載Scheduler整個上下文環(huán)境) sched = instantiate(); return sched; }
2. StdSchedulerFactory.getScheduler().instantiate()
具體實現(xiàn)代碼很多,以下做偽代碼描述
private Scheduler instantiate() throws SchedulerException { // 校驗初始化 if (cfg == null) { initialize(); } // 獲取 Scheduler // 加載 ThreadPool // 加載 JobStore // 加載 DataSources // 加載 SchedulerPlugins // 加載 JobListeners // 加載 TriggerListeners // 加載 ThreadExecutor // 構(gòu)造QuartzScheduler qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry); Scheduler scheduler = instantiate(rsrcs, qs); qs.initialize(); // 返回實例化好的scheduler return scheduler; }
到此這篇關(guān)于Java定時任務(wù)原理詳解的文章就介紹到這了,更多相關(guān)Java定時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合Spring?Boot?Admin實現(xiàn)服務(wù)監(jiān)控的方法
這篇文章主要介紹了SpringBoot整合Spring?Boot?Admin實現(xiàn)服務(wù)監(jiān)控,內(nèi)容包括Server端服務(wù)開發(fā),Client端服務(wù)開發(fā)其中Spring Boot Admin還可以對其監(jiān)控的服務(wù)提供告警功能,如服務(wù)宕機時,可以及時以郵件方式通知運維人員,感興趣的朋友跟隨小編一起看看吧2022-03-03Java 輕松實現(xiàn)二維數(shù)組與稀疏數(shù)組互轉(zhuǎn)
在某些應(yīng)用場景中需要大量的二維數(shù)組來進行數(shù)據(jù)存儲,但是二維數(shù)組中卻有著大量的無用的位置占據(jù)著內(nèi)存空間,稀疏數(shù)組就是為了優(yōu)化二維數(shù)組,節(jié)省內(nèi)存空間2022-04-04設(shè)計模式之責任鏈模式_動力節(jié)點Java學(xué)院整理
這篇文章主要為大家詳細介紹了設(shè)計模式之責任鏈模式的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08springboot中不能獲取post請求參數(shù)的解決方法
這篇文章主要介紹了springboot中不能獲取post請求參數(shù)的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2020-06-06