Java定时任务原理详解

目录
  • 序章
  • 一、Scheduled
    • 1.1 使用方法
    • 1.2 源码分析
  • 二、QUARTZ
    • 2.1 使用方法
    • 2.2 源码分析

序章

定时任务实现方式

当下,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定时任务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java项目实现定时任务的三种方法

    目录 1 使用java.util.Timer 2 使用ScheduledExecutorService 3 使用Spring Task 总结 1 使用java.util.Timer 这种方式的定时任务主要用到两个类,Timer 和 TimerTask,使用起来比较简单.其中 Timer 负责设定 TimerTask 的起始与间隔执行时间. TimerTask是一个抽象类,new的时候实现自己的 run 方法,然后将其丢给 Timer 去执行即可. 代码示例: import java.time.L

  • Java spring定时任务详解

    目录 一.定时任务 1.cron表达式 2.cron示例 3.SpringBoot整合 总结 一.定时任务 1.cron表达式 语法:秒 分 时 日 月 周 年 (其中"年"Spring不支持,也就是说在spring定时任务中只能设置:秒 分 时 日 月 周) 2.cron示例 3.SpringBoot整合 @EnableScheduling @Scheduled 实例: package com.xunqi.gulimall.seckill.scheduled; import lomb

  • Java Spring分别实现定时任务方法

    目录 java实现定时任务 Timer+TimerTask 示例 弊端 ScheduledThreadPoolExecutor 示例 Spring定时任务 示例 原理 java实现定时任务 Jdk自带的库中,有两种方式可以实现定时任务,一种是Timer,另一种是ScheduledThreadPoolExecutor. Timer+TimerTask 创建一个Timer就创建了一个线程,可以用来调度TimerTask任务 Timer有四个构造方法,可以指定Timer线程的名字以及是否设置为为守护线

  • Java之SpringBoot定时任务案例讲解

    1. SpringBoot--任务:定时任务 项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候, 分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式,提供了 两个接口和两个注解,并且用corn表达式去定时. TaskScheduler //任务调度程序 TaskExecutor //任务执行者 @EnableScheduling //开启定时功能的注解,放在主入口 @Scheduled //什么时候执行 cron表达式 1.1 编写定时任务的方法 我们里面存在一

  • Java实现定时任务最简单的3种方法

    目录 一.Timer 二.ScheduledExecutorService 三.Spring Task 1.开启定时任务 2.添加定时任务 Cron 表达式 知识扩展:分布式定时任务 1.ZSet 实现方式 2.键空间通知 总结 一.Timer Timer是JAVA自带的定时任务类,实现如下: public class MyTimerTask { public static void main(String[] args) { // 定义一个任务 TimerTask timerTask = ne

  • Java 实现定时任务的三种方法

    是的,不用任何框架,用我们朴素的 Java 编程语言就能实现定时任务. 今天,栈长就介绍 3 种实现方法,教你如何使用 JDK 实现定时任务! 1. sleep 这也是我们最常用的 sleep 休眠大法,不只是当作休眠用,我们还可以利用它很轻松的能实现一个简单的定时任务. 实现逻辑: 新开一个线程,添加一个 for/ while 死循环,然后在死循环里面添加一个 sleep 休眠逻辑,让程序每隔 N 秒休眠再执行一次,这样就达到了一个简单定时任务的效果. 实现代码如下: private stat

  • Java定时任务原理详解

    目录 序章 一.Scheduled 1.1 使用方法 1.2 源码分析 二.QUARTZ 2.1 使用方法 2.2 源码分析 序章 定时任务实现方式 当下,java编码过程中,实现定时任务的方式主要以以下两种为主 spring框架的@Scheduled quzrtz框架 网络上关于这两种框架的实践和配置相关的教程很多,这里不再赘述. 本文主要就二者的框架原理实现做一个入门引导,为了解深层实现细节做一定的铺垫. 本文源码版本 spring-context-3.2.18.RELEASE.jar qu

  • Java ShutdownHook原理详解

    ShutdownHook介绍 在java程序中,很容易在进程结束时添加一个钩子,即ShutdownHook.通常在程序启动时加入以下代码即可 Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run() { System.out.println("I'm shutdown hook..."); } }); 有了ShutdownHook我们可以 在进程结束时做一些善后工作,例如释放占用的资源,

  • Java序列化原理详解

    前言 关于序列化的几种疑问? 什么是序列化?工作中什么时候用到序列化了? 为什么实现了java.io.Serializable接口就能序列化? java中serialVersionUID 为什么不能改变? Serializable序列化和json序列化有什么关系? 你都会哪几种对象深拷贝方式? 以上抛出了几个问题,大家都能回答上来吗?回答不上来的话就接着往下看吧. 前提知识: 讲解之前先扩充一些前提知识. 二进制协议和文本协议 首先我们要知道所有的数据在底层的传输都是二进制流,这点是毋庸置疑的.

  • Java线程池FutureTask实现原理详解

    前言 线程池可以并发执行多个任务,有些时候,我们可能想要跟踪任务的执行结果,甚至在一定时间内,如果任务没有执行完成,我们可能还想要取消任务的执行,为了支持这一特性,ThreadPoolExecutor提供了 FutureTask 用于追踪任务的执行和取消.本篇介绍FutureTask的实现原理. 类视图 为了更好的理解FutureTask的实现原理,这里先提供几个重要接口和类的结构,如下图所示: RunnableAdapter ThreadPoolExecutor提供了submit接口用于提交任

  • java并发等待条件的实现原理详解

    前言 前面介绍了排它锁,共享锁的实现机制,本篇继续学习AQS中的另外一个内容-Condition.想必学过java的都知道Object.wait和Object.notify,同时也应该知晓这两个方法的使用离不开synchronized关键字.synchronized是jvm级别提供的同步原语,它的实现机制隐藏在jvm实现中.作为Lock系列功能中的Condition,就是用来实现类似 Object.wait和Object.notify 对应功能的. 使用场景 为了更好的理解Lock和Condit

  • Java中synchronized实现原理详解

    记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字"同步",也成为了我们解决多线程情况的百试不爽的良药.但是,随着我们学习的进行我们知道synchronized是一个重量级锁,相对于Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它. 诚然,随着Javs SE 1.6对synchronized进行的各种优化后,synchronized并不会显得那么

  • java进行远程部署与调试及原理详解

    这篇文章主要介绍了java进行远程部署与调试及原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 远程调试,特别是当你在本地开发的时候,你需要调试服务器上的程序时,远程调试就显得非常有用. JAVA 支持调试功能,本身提供了一个简单的调试工具JDB,支持设置断点及线程级的调试同时,不同的JVM通过接口的协议联系,本地的Java文件在远程JVM建立联系和通信.此篇是Intellij IDEA远程调试的教程汇总和原理解释,知其然而又知其所以然.

  • Java Iterator接口遍历单列集合迭代器原理详解

    这篇文章主要介绍了Java Iterator接口遍历单列集合迭代器原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Iterator接口概述 在程序开发中,经常需要遍历集合中的所有元素.针对这种需求,JDK专门提供了一个接口java.util.Iterator . Iterator 接口也是Java集合中的一员,但它与 Collection . Map 接口有所不同,Collection 接口与 Map 接口主要用于存储元素,而 Iter

  • Java静态static关键字原理详解

    这篇文章主要介绍了Java静态static关键字原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 static关键字既可以修饰成员变量,也可以修改成员方法,修饰的成员变量和成员方法可以直接通过类名调用,也可以通过对象调用(其实即使是通过对象调用,也会被翻译成类名调用),建议通过类名调用. 成员方法用static修饰后,就成为了静态方法,静态方法不属于对象,而是属于类. 注意事项: 1.静态方法中不能使用this,因为this指的是当前对象

  • Java代码块与代码加载顺序原理详解

    这篇文章主要介绍了Java代码块与代码加载顺序原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本文首先介绍几个基本的名次,然后介绍了三种代码块的特性和使用方法. 在面试大型公司时,如果遇到大型国企或者大的互联网私企,笔试中经常遇到代码块和代码加载顺序的笔试题.这里做一个总结,也方便各位小伙伴飙车不会飘. 名词解释 代码块 由 { } 包起来的代码,称为代码块 静态代码块 由 static { } 包起来的代码,称为静态代码块. 不同类型

随机推荐