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

