zoukankan      html  css  js  c++  java
  • Spring 使用介绍(十二)—— Spring Task

    一、概述

    1、jdk的线程池和任务调用器分别由ExecutorService、ScheduledExecutorService定义,继承关系如下:

    ThreadPoolExecutor:ExecutorService的实现类,其构造函数提供了灵活的参数配置,可构造多种类型的线程池,详细可参考JAVA进阶----ThreadPoolExecutor机制

    ScheduledThreadPoolExecutor:ScheduledExecutorService的实现类,用于任务调度

    2、spring task对定时任务的两个抽象:

    • TaskExecutor:与jdk中Executor相同,引入的目的是为定时任务的执行提供线程池的支持
    • TaskScheduler:对定时任务的抽象

    继承关系如下:

    任务执行器与调度器的实现类分别为ThreadPoolTaskExecutor、ThreadPoolTaskScheduler

    TaskScheduler需要传入一个Runnable的任务做为参数,并指定需要周期执行的时间或者触发器(Trigger)。

    spring定义了Trigger接口的实现类CronTrigger,支持使用cron表达式指定定时策略,使用如下:

    scheduler.schedule(task, new CronTrigger("30 * * * * ?"));

    二、定时任务

    spring定时任务的使用和配置非常简单,支持xml配置和注解两个方式

    1、XML配置方式

    任务类

    @Component
    public class TestTask {
        public void job() {
            System.out.println("hello matt!");
        }
    }

    配置

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans  
        xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:task="http://www.springframework.org/schema/task" 
        xsi:schemaLocation="  
        http://www.springframework.org/schema/beans        
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.2.xsd 
        http://www.springframework.org/schema/task 
        http://www.springframework.org/schema/task/spring-task-4.1.xsd">  
    
        <context:component-scan base-package="cn.matt.schedule"/>  
    
         <!-- 任务调度器-->
         <task:scheduler id="myScheduler" pool-size="10"/> 
    
        <!-- 任务配置-->
        <task:scheduled-tasks scheduler="myScheduler"> 
            <task:scheduled ref="testTask" method="job" cron="0/2 * * * * ?"/> 
         </task:scheduled-tasks> 
    </beans>  

    测试

    public class TaskTest {
        @Test
        public void test() throws InterruptedException {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-context.xml");
            TimeUnit.SECONDS.sleep(10);
        }
    }

    2、注解方式

     任务类

    @Component
    public class TestTask {
        @Scheduled(cron = "0/2 * * * * ?")
        public void job() {
            System.out.println("hello matt!");
        }
    }

    配置

    <!-- 任务调度器-->
    <task:scheduler id="myScheduler" pool-size="10"/> 
    
    <!-- 开启任务注解-->
    <task:annotation-driven scheduler="myScheduler"/> 

    测试代码与xml配置方式相同

    补充说明:

    • 使用<task:executor>配置任务执行器,即实例化ThreadPoolTaskExecutor
    • 使用<task:scheduler>配置任务调度器,即实例化ThreadPoolTaskScheduler
    • 两种方式的任务调度器不指定时,默认会使用只有一个线程的调用器,关于配置的详细介绍和默认参数,可参考xsd文档 http://www.springframework.org/schema/task/spring-task-4.1.xsd

    疑问:

    有些项目同时指定任务执行器和调度器,如下:

    <!-- 启用注解驱动的定时任务 -->
    <task:annotation-driven scheduler="myScheduler" executor="myExecutor"/>
    <!-- 配置定时任务的线程池 -->
    <task:scheduler id="myScheduler" pool-size="10"/>  
    <task:executor id="myExecutor" pool-size="10" />  

    存在两个线程池,两者的关系是怎样的?执行器用的是哪个线程池?

    经验证,定时任务执行时,使用的是任务调度器的线程池,任务执行器的设置对定时任务的执行没有影响,测试代码如下:

    任务类

    @Component
    public class TestTask {
        @Scheduled(cron = "0/5 * * * * ?")
        public void job1() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println(String.format("%s*******%s", sdf.format(new Date()), "job1"));
        }
        
        @Scheduled(cron = "0/10 * * * * ?")
        public void job2() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println(String.format("%s*******%s", sdf.format(new Date()), "job2"));
            
            try {
                TimeUnit.SECONDS.sleep(15);
            } catch (InterruptedException e) {}
        }
    }
    View Code

     配置与疑问中相同

    测试(测试时,通过改变线程池的大小进行验证)

    public class TaskTest {
        @Test
        public void test() throws InterruptedException {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-context.xml");
            TimeUnit.SECONDS.sleep(1000);
        }
    }
    View Code

    3、cron表达式

    spring支持6个参数的cron表达式,格式如下:

    {秒} {分} {时} {日期} {月} {星期}
    • 秒:必填项,允许的值范围是0-59,支持的特殊符号包括'-*/,',','表示特定的某一秒才会触发任务,'-'表示一段时间内会触发任务,'*'表示每一秒都会触发,'/'表示从哪一个时刻开始,每隔多长时间触发一次任务。
    • 分:必填项,允许的值范围是0-59,支持的特殊符号和秒一样,含义类推
    • 时:必填项,允许的值范围是0-23,支持的特殊符号和秒一样,含义类推
    • 日期:必填项,允许的值范围是1-31,支持的特殊符号相比秒多了?,表示与{星期}互斥,即意味着若明确指定{星期}触发,则表示{日期}无意义,以免引起冲突和混乱
    • 月:必填项,允许的值范围是1-12(JAN-DEC),支持的特殊符号与秒一样,含义类推
    • 星期:必填项,允许值范围是1~7 (SUN-SAT),1代表星期天(一星期的第一天),以此类推,7代表星期六,支持的符号相比秒多了?,表达的含义是与{日期}互斥,即意味着若明确指定{日期}触发,则表示{星期}无意义。

    示例:

    0 0 12 * * ?          每天中午12点触发 
    0 * 14 * * ?          每天下午2点到下午2:59期间的每1分钟触发 
    0 0/5 14 * * ?        每天下午2点到下午2:55期间的每5分钟触发 
    0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发 

    关于cron表达式的详细介绍可参考spring定时任务详解

    三、异步调用

    spring提供@Async注解,可很方便的实现异步调用,简单示例如下:

    接口及实现类

    public interface Hello {
        void doSomething1();
        void doSomething2();
    }
    @Component
    public class HelloImpl implements Hello {
        @Async
        @Override
        public void doSomething1() {
            System.out.println(String.format("thread:%d **** doSomething1", Thread.currentThread().getId()));
    
        }
        
        @Async("myExecutor2")
        @Override
        public void doSomething2() {
            System.out.println(String.format("thread:%d **** doSomething2", Thread.currentThread().getId()));
        }
    }

    配置

    <context:component-scan base-package="cn.matt.schedule"/>
    <!-- 开启@Async注解支持 -->
    <task:annotation-driven executor="myExecutor1"/>
    <!-- 定义执行器 -->
    <task:executor id="myExecutor1" pool-size="10" /> 
    <task:executor id="myExecutor2" pool-size="10" /> 

    测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-context.xml")
    public class SpringTestBase {}
    public class AsyncTest extends SpringTestBase {
        @Autowired
        private Hello hello;
    
        @Test
        public void test() {
            System.out.println(String.format("main thread:%d **** doSomething1", Thread.currentThread().getId()));
            
            hello.doSomething1();
            hello.doSomething2();
        }
    }
    
    // 输出:
    // main thread:1 **** doSomething1
    // thread:20 **** doSomething2
    // thread:19 **** doSomething1

    说明:@Async默认使用<task:annotation-driven/>指定的执行器,当存在多个执行器时,可通过@Async的value属性单独指定

    参考:

    Spring任务调度之Spring-Task

    spring定时任务详解(@Scheduled注解)

    深入浅出Spring task定时任务

     Spring异步任务处理,@Async的配置和使用

    Spring中@Async

  • 相关阅读:
    CentOS下设置ipmi
    CentOS 使用文件增加Swap空间
    CentOS LVM
    做IT需要掌握的电力基础知识
    CentOS 7搭建本地yum源
    Lsi卡和IB卡在CentOS中升级
    Mellanox 4036配置
    IdentityServer4入门二
    IdentityServer4入门一
    RAFT选举算法-分布式数据库困惑
  • 原文地址:https://www.cnblogs.com/MattCheng/p/9052140.html
Copyright © 2011-2022 走看看