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一些原码.多看看 ...
随机推荐
- Jmeter接口测试流程详解(中科软测认证中心)
1.jmeter简介 Jmeter是由Apache公司开发的java开源项目,所以想要使用它必须基于java环境才可以: Jmeter采用多线程,允许通过多个线程并发取样或通过独立的线程对不同的功能同 ...
- 为什么 io 包一般以 byte 数组做为处理单位?
为什么 io 包一般以 byte 数组做为处理单位? 本文写于 2021 年 9 月 7 日 编程语言中时常会出现 []byte 作为类型的操作.特别是在网络传输或是 io 操作中,例如 socket ...
- kvm 虚拟化技术 1.1 安装
1.·VMware开启虚拟化设置 2.安装一些虚拟化的必备插件 yum install -y qemu-kvm qemu-kvm-tools libvirt virt-manager virt-ins ...
- python之数据类型的内置方法(str, list)
目录 字符串的内置方法 移除首尾指定字符 字母大小写相关操作 判断字符串的开头或结尾是否是指定字符 字符串特殊的输出方法 拼接字符串 替换指定字符 判断是否是纯数字 查找指定字符对应的索引值 文本位置 ...
- Elasticsearch(es)介绍与安装
### RabbitMQ从入门到集群架构: https://zhuanlan.zhihu.com/p/375157411 可靠性高 ### Kafka从入门到精通: https://zhuanlan. ...
- 聊聊 C# 中的多态底层 (虚方法调用) 是怎么玩的
最近在看 C++ 的虚方法调用实现原理,大概就是说在 class 的首位置存放着一个指向 vtable array 指针数组 的指针,而 vtable array 中的每一个指针元素指向的就是各自的 ...
- select into 与 insert into 的区别
1.select * into table_A from table_B table_A是个新创建表,table_B是个已经存在的表. 2.insert into table_A from tabl ...
- Java - 六原则一法则
Java - 六原则一法则 单一职责原则:一个类只做它该做的事情.(单一职责原则想表达的就是"高内聚",写代码最终极的原则只有六个字"高内聚.低耦合",所谓的高 ...
- SQL Server之自动创建视图
本方法只适合特定模式的视图创建. 比如,创建需要整张表列名的视图,或者当表和需要的列名统计在一张数据表当中,如图所示: 首先要先获取要创建视图所需要的表,这里我获取的是整个数据库中的表, IF OBJ ...
- Git使用 - 忽略特定文件 - gitignore
1. 背景 2. 创建.gitignore 文件 3. 文件内容样式 4. exclude文件 5. gitignore 文件模板 6. 参考文档 1. 背景 前提知识:在工作目录下的每一个文件都不外 ...