zoukankan      html  css  js  c++  java
  • Spring的任务调度@Scheduled注解——task:scheduler和task:executor的解析

    一个简单的Spring定时任务的 demo,全部代码见下载地址:https://download.csdn.net/download/yx0628/10511753
    对于 applicationContext 的配置如下:调度器线程池 task:scheduler 和 task:executor 的意义在后边例子中会详细的测试和说明。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-4.1.xsd"
    xmlns:task="http://www.springframework.org/schema/task">

    <context:annotation-config />

    <task:annotation-driven scheduler="myScheduler" executor="myExecutor"/>

    <!-- 调度线程池配置 -->
    <task:scheduler id="myScheduler" pool-size="5"/>
    <!-- 执行线程池配置 -->
    <task:executor id="myExecutor" pool-size="5"/>

    <context:component-scan base-package="com.zaimeibian" />

    </beans>a
    package com.zaimeibian.task;

    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;

    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;

    @Component
    public class PrintTask {

    DateFormat df = new SimpleDateFormat("HH:mm:ss");

    // 这个Async注解,代表当前任务是要异步执行的
    @Async
    @Scheduled(fixedRate = 5000)
    public void printA(){
    System.out.println("A执行 " + df.format(new Date()));
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    }
    System.out.println("A打印输出 " + df.format(new Date())+ Thread.currentThread());
    }

    @Scheduled(fixedRate = 5000)
    public void printB(){
    System.out.println("B执行 " + df.format(new Date()));
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    }
    System.out.println("B打印输出 " + df.format(new Date())+ Thread.currentThread());
    }

    @Scheduled(fixedRate = 5000)
    public void printC(){
    System.out.println("C执行 " + df.format(new Date()));
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    }
    System.out.println("C打印输出 " + df.format(new Date())+ Thread.currentThread());
    }

    // 配置initialDelay的任务是在容器启动后延迟一定时间才开始调度
    @Scheduled(fixedRate = 5000, initialDelay=1000)
    public void printD(){
    System.out.println("D执行 " + df.format(new Date()));
    try {
    Thread.sleep(30000);
    } catch (InterruptedException e) {
    }
    System.out.println("D打印输出 " + df.format(new Date())+ Thread.currentThread());
    }

    // 配置initialDelay的任务是在容器启动后延迟一定时间才开始调度
    @Scheduled(fixedRate = 5000, initialDelay=1000)
    public void printE(){
    System.out.println("E执行 " + df.format(new Date()));
    try {
    Thread.sleep(30000);
    } catch (InterruptedException e) {
    }
    System.out.println("E打印输出 " + df.format(new Date())+ Thread.currentThread());
    }

    }

    这里,fixDelay 和 fixRate 参数代表每个任务,前者在上一个任务调度完成后,延迟一定的时间执行。而后者可以在每间隔一定时间就执行新任务(但这里与 executor 的参数有关)。下面的试验会详细说明这两个参数。
    Spring 的任务调度线程池,即

    <task:scheduler id="myScheduler" pool-size="5"/>
    1
    如果不配置,那么默认值是 1 ,即结果是所有声明的任务,都是串行执行的,比如代码中的 A/B/C/D/E 五个任务,在默认值是 1 的情况下,只能一个个串行来执行。不能出现并行的情况。
    可以将参数改为 1 ,运行,输出如下:

    B执行 12:16:36
    B打印输出 12:16:46Thread[myScheduler-1,5,main]
    A执行 12:16:46
    A打印输出 12:16:56Thread[myScheduler-1,5,main]
    C执行 12:16:56
    C打印输出 12:17:06Thread[myScheduler-1,5,main]
    D执行 12:17:06
    D打印输出 12:17:36Thread[myScheduler-1,5,main]
    E执行 12:17:36
    E打印输出 12:18:06Thread[myScheduler-1,5,main]
    B执行 12:18:06
    B打印输出 12:18:16Thread[myScheduler-1,5,main]
    A执行 12:18:16
    A打印输出 12:18:26Thread[myScheduler-1,5,main]
    C执行 12:18:26
    C打印输出 12:18:36Thread[myScheduler-1,5,main]
    D执行 12:18:36
    D打印输出 12:19:06Thread[myScheduler-1,5,main]
    E执行 12:19:06
    E打印输出 12:19:36Thread[myScheduler-1,5,main]
    B执行 12:19:36
    B打印输出 12:19:46Thread[myScheduler-1,5,main]
    A执行 12:19:46
    A打印输出 12:19:56Thread[myScheduler-1,5,main]

    可以看到只有 myScheduler-1 这一个调度线程来调度这五个任务,任务之间只能串行,即等待上个任务完成后释放调度线程,然后调度线程才能调度执行下一个任务。

    然后我们还改回调度线程池 5 个线程池大小,运行:

    C执行 12:23:04
    A执行 12:23:04
    B执行 12:23:04
    E执行 12:23:05
    D执行 12:23:05
    C打印输出 12:23:14Thread[myScheduler-2,5,main]
    C执行 12:23:14
    A打印输出 12:23:14Thread[myScheduler-3,5,main]
    A执行 12:23:14
    B打印输出 12:23:14Thread[myScheduler-1,5,main]
    B执行 12:23:14
    C打印输出 12:23:24Thread[myScheduler-2,5,main]
    C执行 12:23:24
    A打印输出 12:23:24Thread[myScheduler-3,5,main]
    A执行 12:23:24
    B打印输出 12:23:24Thread[myScheduler-1,5,main]
    B执行 12:23:24
    C打印输出 12:23:34Thread[myScheduler-2,5,main]
    A打印输出 12:23:34Thread[myScheduler-3,5,main]
    C执行 12:23:34
    A执行 12:23:34
    B打印输出 12:23:34Thread[myScheduler-1,5,main]
    B执行 12:23:34
    E打印输出 12:23:35Thread[myScheduler-4,5,main]
    E执行 12:23:35
    D打印输出 12:23:35Thread[myScheduler-5,5,main]
    D执行 12:23:35
    可以看到,如果每个任务都有一个调度线程来处理,那么就是很理想的情况,各个任务之间是并行的,互不干扰各自独立,按照各自的时间来触发。(可以看到 1-5 这 5 个线程都在各自调度自己的任务)
    这里还要注意一点,fixDelay 和 fixRate 看上去似乎是一样的,在每个任务的调度线程中,都是必须上一个执行完毕后,等待配置的时间后,再开始下一次的执行。是不是 fixRate 参数不起作用呢?因为不是说 fixRate 是间隔一定时间执行,而不需要等待上一个任务执行完毕么?

    这里引入另一个参数,可以看任务 A 上方注释掉的 @Async 注解:这个注解,代表可以异步执行。异步执行的话,调度线程池就会不用当前调度线程来执行,而是交给 task:executor 这个执行线程池来执行。
    我们来运行,这里为了更好的说明,我们可以把 A 的 fixRate 改为 2秒 ,看运行结果:

    B执行 12:34:44
    C执行 12:34:44
    A执行 12:34:44
    D执行 12:34:45
    E执行 12:34:45
    A执行 12:34:46
    A执行 12:34:48
    A执行 12:34:50
    A执行 12:34:52
    B打印输出 12:34:54Thread[myScheduler-2,5,main]
    B执行 12:34:54
    C打印输出 12:34:54Thread[myScheduler-3,5,main]
    A打印输出 12:34:54Thread[myExecutor-1,5,main]
    C执行 12:34:54
    A执行 12:34:54
    A打印输出 12:34:56Thread[myExecutor-2,5,main]
    A执行 12:34:56
    A打印输出 12:34:58Thread[myExecutor-3,5,main]
    A执行 12:34:58
    A打印输出 12:35:00Thread[myExecutor-4,5,main]
    A执行 12:35:00
    A打印输出 12:35:02Thread[myExecutor-5,5,main]
    A执行 12:35:02
    B打印输出 12:35:04Thread[myScheduler-2,5,main]
    B执行 12:35:04
    C打印输出 12:35:04Thread[myScheduler-3,5,main]
    A打印输出 12:35:04Thread[myExecutor-1,5,main]
    C执行 12:35:04
    A执行 12:35:04
    A打印输出 12:35:06Thread[myExecutor-2,5,main]
    这里 A 任务的线程是 myExecutor-1 到 myExecutor-5,说明 myScheduler-1 这个调度线程调度了 A 任务,但是交给了线程池中的 myExecutor 中的执行线程来具体执行的。
    所以,配置 task:scheduler 参数的线程池,是为了根据任务总数来分配调度线程池的大小;而配置 task:executor ,是为了某个任务如果要异步的执行时,实现当前任务内的多线程并发。

    作者: lost blog

    出处: http://www.cnblogs.com/JAYIT/

    关于作者:专注服务器端开发

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接 如有问题, 可邮件(sawyershaw@qq.com)咨询.

  • 相关阅读:
    webpack 3.x loader
    git忽略已经被提交的文件,以及如何恢复追踪
    sessionstorage:本地临时存储
    es6中顶层对象属性≠全局属性
    Android DRM
    FFMPEG中关于ts流的时长估计的实现
    整理mp4协议重点,将协议读薄
    LOCAL_SHARED_LIBRARIES 与 LOCAL_LDLIBS,LOCAL_LDFLAGS的区别
    valgrind调查内存leak
    Android中*_handle_t/ANativeWindowBuffer/ANativeWindow/GraphicBuffer/Surface的关系
  • 原文地址:https://www.cnblogs.com/JAYIT/p/14606970.html
Copyright © 2011-2022 走看看