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一些原码.多看看 ...
随机推荐
- 栈在go语言中实现,及解决388.文件的最长绝对路径的思路
今天在LeetCode刷每日一题,遇到了388. 文件的最长绝对路径的思路,这道题让我想到了系统的目录是栈结构,果然在题解中找到了栈的解法(暴力半天没出来,跑去看题解了QWQ). 所以我就捎带复习了一 ...
- 896.Montonic Array - LeetCode
Question 896. Monotonic Array Solution 题目大意: 类似于数学中的减函数,增函数和物理中的加速度为正或为负 思路: 先比较前两个是大于0还是小于0,如果等于0就比 ...
- 114_Power Pivot 销售订单之销售额、成本、利润率相关
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 双十二回来后遇到一个比较有意思的计算销售额和利润率的需求(见下文说明). 先看下效果. 结果 说明: 1.订单表 ...
- [SQLServer]NetCore中将SQLServer数据库备份为Sql脚本
NetCore中将SQLServer数据库备份为Sql脚本 描述: 最近写项目收到了一个需求, 就是将SQL Server数据库备份为Sql脚本, 如果是My Sql之类的还好说, 但是在网上搜了一大 ...
- vue 的个人学习小笔记
一.vite2.0+vue3.0+ts 创建.配置 个人公众号文章地址 个人github仓库地址 1.Vite 创建 vue3 项目: 1.1.npm 常用命令 1.npm 查看版本号 npm vie ...
- React简单教程-2-ts和组件参数
前言 在上一章:React 简单教程-1-组件 我们知道了 React 的组件是什么,长什么样,用 js 和 HTML 小小体验了一下组件.在这一章,我们将使用 typescript(简称 ts) 来 ...
- CSS(九):background(背景属性)
通过CSS背景属性,可以给页面元素添加背景样式. 背景属性可以设置背景颜色.背景图片.背景平铺.背景图像固定等. background-color(背景颜色) background-color属性定义 ...
- 10.Linux防火墙iptables之SNAT与DNAT
Linux防火墙iptables之SNAT与DNAT 目录 Linux防火墙iptables之SNAT与DNAT SNAT策略及应用 SNAT策略概述 SNAT策略典型应用环境 SNAT策略原理 SN ...
- MES 系统介绍
MES系统是一套面向制造企业车间执行层的生产信息化管理系统.MES可以为企业提供包括制造数据管理.计划排程管理.生产调度管理.库存管理.质量管理.人力资源管理.工作中心/设备管理.工具工装管理.采购管 ...
- Day05 表格
表格 <table width="300" border="1" cellspacing="0"> <caption> ...