本文适用语言: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定时任务原理入门的更多相关文章

  1. 大白话说Java反射:入门、使用、原理

    文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java反射:入门.进阶.原理> 反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释. 一般情况下,我们使用某个类时 ...

  2. 大白话说Java泛型:入门、使用、原理

    文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型:入门.使用.原理> 远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的.当时 Java 程序员们写集合类的代码都 ...

  3. Java学习---Quartz定时任务快速入门

    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运行十个,百个, ...

  4. 大白话说Java反射:入门、使用、原理 (转)

    文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java反射:入门.进阶.原理> 目录 一个简单的例子 反射常用API 获取反射中的Class对象 通过反射创建类对象 通过反射获取类 ...

  5. atititt.java定时任务框架选型Spring Quartz 注解总结

    atititt.java定时任务框架选型Spring Quartz 总结 1. .Spring Quartz  (ati recomm) 1 2. Spring Quartz具体配置 2 2.1. 增 ...

  6. Java入门-浅析Java学习从入门到精通【转】

    一. JDK (Java Development Kit)  JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具和Java基础的类库 ...

  7. Java编译原理

    http://wenku.baidu.com/view/f9b1734b87c24028915fc3a3.html Java编译原理 1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想 ...

  8. Java对象序列化入门

      Java对象序列化入门 关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结.此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制 ...

  9. java J2EE学习入门

    首先学习JAVA基础编程,大学教材就是最简单的了!象写写Helloworld啊 输出水仙花数啊 玩些简单的,慢慢在研究研究流啊,都可以了.然后学习简单的JSP,这个时候多上网上DOWN一些原码.多看看 ...

随机推荐

  1. Next.js 在 Serverless 中从踩坑到破茧重生

    作者 杨苏博,偏后端的全栈开发,目前负责腾云扣钉的 Cloud Studio 产品.在团队中负责接技术架构设计与 Review.Cloud Studio 编辑器内核设计与开发.部分核心插件设计与开发: ...

  2. Mockito+Junit5单元测试

    参考: https://segmentfault.com/a/1190000006746409 https://waylau.com/mockito-quick-start/ 1.引入依赖 下面这个最 ...

  3. UNIAPP实现PDA扫码

    目前我接触到了两种方法,以扫码pda安卓采集器(可以直接理解为手机上有个激光扫码)的设置划分. 1.扫描设置 --> 键盘方式输出(键盘类型:物理键盘),注意设置要看具体的型号: 2.扫码设置 ...

  4. 斯坦福NLP课程 | 第11讲 - NLP中的卷积神经网络

    作者:韩信子@ShowMeAI,路遥@ShowMeAI,奇异果@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www. ...

  5. install dns server on ubuntu

    参考 CSDN/Ubuntu环境下安装和配置DNS服务器 在 Ubuntu 上安裝 DNS server Install BIND 9 on Ubuntu and Configure It for U ...

  6. # k8s-jenkins在kubernetes中持续部署

    k8s-jenkins在kubernetes中持续部署 1. k8s-jenkins在kubernetes中持续部署 Kubernetes Continuous Deploy插件:用于将资源配置部署到 ...

  7. 《Mybatis 手撸专栏》第9章:细化XML语句构建器,完善静态SQL解析

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你只是在解释过程,而他是在阐述高度! 如果不是长时间的沉淀.积累和储备,我一定也没有 ...

  8. JavaSE_多线程入门 线程安全 死锁 状态 通讯 线程池

    1 多线程入门 1.1 多线程相关的概念 并发与并行 并行:在同一时刻,有多个任务在多个CPU上同时执行. 并发:在同一时刻,有多个任务在单个CPU上交替执行. 进程与线程 进程:就是操作系统中正在运 ...

  9. 《HALCON数字图像处理》第六章笔记

    目录 第六章 图像增强 图像增强的概念和分类 灰度变换 直方图处理 图像的平滑 图像的锐化 图像的彩色增强 我在Gitee上建了个仓库,会将学习书本的时候打的一些代码上传上去,笔记中所有代码都在仓库里 ...

  10. 【Redis】哨兵初始化和主观下线

    在的redis启动函数main(server.c文件)中,对哨兵模式进行了检查,如果是哨兵模式,将调用initSentinelConfig和initSentinel进行初始化,initServer函数 ...