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.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;
}
JAVA定时任务原理入门的更多相关文章
- 大白话说Java反射:入门、使用、原理
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java反射:入门.进阶.原理> 反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释. 一般情况下,我们使用某个类时 ...
- 大白话说Java泛型:入门、使用、原理
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型:入门.使用.原理> 远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的.当时 Java 程序员们写集合类的代码都 ...
- Java学习---Quartz定时任务快速入门
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运行十个,百个, ...
- 大白话说Java反射:入门、使用、原理 (转)
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java反射:入门.进阶.原理> 目录 一个简单的例子 反射常用API 获取反射中的Class对象 通过反射创建类对象 通过反射获取类 ...
- atititt.java定时任务框架选型Spring Quartz 注解总结
atititt.java定时任务框架选型Spring Quartz 总结 1. .Spring Quartz (ati recomm) 1 2. Spring Quartz具体配置 2 2.1. 增 ...
- Java入门-浅析Java学习从入门到精通【转】
一. JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具和Java基础的类库 ...
- Java编译原理
http://wenku.baidu.com/view/f9b1734b87c24028915fc3a3.html Java编译原理 1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想 ...
- Java对象序列化入门
Java对象序列化入门 关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结.此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制 ...
- java J2EE学习入门
首先学习JAVA基础编程,大学教材就是最简单的了!象写写Helloworld啊 输出水仙花数啊 玩些简单的,慢慢在研究研究流啊,都可以了.然后学习简单的JSP,这个时候多上网上DOWN一些原码.多看看 ...
随机推荐
- @Inherited 原注解功能介绍
@Inherited 底层 package java.lang.annotation; /** * Indicates that an annotation type is automatically ...
- 一图详解java-class类文件原理
摘要:徒手制作一张超大的类文件解析图,方便通过浏览这个图能马上回忆起class文件的结构以及内部的指令. 本文分享自华为云社区<[读书会第十二期]这可能是全网"最大".&qu ...
- linux篇-linux下zabbix安装
1本人用的是apache+mysql+php 2下载zabbix软件包,官网下载 https://sourceforge.net/projects/zabbix/files/ZABBIX Latest ...
- yolov5训练自定义数据集
yolov5训练自定义数据 step1:参考文献及代码 博客 https://blog.csdn.net/weixin_41868104/article/details/107339535 githu ...
- 【freertos】009-任务控制
目录 前言 9.1 相对延时 9.1.1 函数原型 9.1.2 函数说明 9.1.3 参考例子 9.2 绝对延时 9.2.1 函数原型 9.2.2 函数说明 9.2.3 参考例子 9.3 获取任务优先 ...
- CabloyJS 基于 EggJS 实现的模块编译与发布
背景 现在,EggJS被许多开发团队所采用.有的团队基于商业知识产权的考量,往往会提一个问题:是否可以把EggJS当中的代码编译打包,然后再把代码丑化? 模块编译的机制 EggJS为何不能便利的实现编 ...
- 从零开始实现lmax-Disruptor队列(二)多消费者、消费者组间消费依赖原理解析
MyDisruptor V2版本介绍 在v1版本的MyDisruptor实现单生产者.单消费者功能后.按照计划,v2版本的MyDisruptor需要支持多消费者和允许设置消费者组间的依赖关系. 由于该 ...
- 机器学习中 TP FP TN FN的概念
二分类 在二分类问题中,TP FP TN FN 是非常清楚且易于理解的. TP (True Positive) : 预测为 1 ,真实值也为 1 -> 真阳性 FP (False Positiv ...
- Windows-安装OpenVINO
安装指导书链接: https://docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_wi ...
- JavaScript 防盗链的原理以及破解方法
先说说防盗链的原理,http 协议中,如果从一个网页跳到另一个网页,http 头字段里面会带个 Referer.这里的Referer是由于历史原因导致了拼写错误 后来也就一直沿用.图片服务器通过检测 ...