spring定时任务详解spring schedule和spring-quartz_赶路人儿的博客-程序员宅基地

技术标签: spring  java  mybatis  

从实现的技术上来分类,java定时任务目前主要有三种:

  1. Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行;而且作业类需要集成java.util.TimerTask,一般用的较少。
  2. Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行;使用起来需要继承org.springframework.scheduling.quartz.QuartzJobBean,配置稍显复杂,所以,一般会使用spring集成quartz,稍后会详细介绍;
  3. Spring3.0以后自带的task,即:spring schedule,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

综上,spring中使用定时任务有两种方式:spring schedule和spring-quartz,接下来我们重点介绍这两种。使用前都需要引入spring的包。

<!-- spring -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
	<version>${org.springframework.version}</version>
	<type>jar</type>
	<scope>compile</scope>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-beans</artifactId>
	<version>${org.springframework.version}</version>
	<type>jar</type>
	<scope>compile</scope>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>${org.springframework.version}</version>
	<type>jar</type>
	<scope>compile</scope>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>${org.springframework.version}</version>
</dependency>

spring schedule

1、xml配置的方式:

1)application.xml:

首先在application.xml中引入task的命名空间,以及通过task标签定义任务。

<?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:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">

    <task:scheduler id="myScheduler"/>
    <task:scheduled-tasks scheduler="myScheduler">
        <task:scheduled ref="doSomethingTask" method="doSomething" cron="0 * * * * *"/>
    </task:scheduled-tasks>
</beans>

2)任务类:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DoSomethingTask {

    public void doSomething() {
        System.out.println("do something");
    }
}

2、@schedule 注解方式:

1)application.xml

同样在application.xml中引入task的命名空间,以及启用注解驱动的定时任务。

<?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:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">

    <task:annotation-driven/> 
</beans>

2)任务类:

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

@Component
public class DoSomethingTask {
    @Scheduled(cron="0 * * * * *")
    public void doSomething() {
        System.out.println("do something");
    }
}

3、Cron表达式:

由6~7项组成,中间用空格分开。从左到右依次是:秒、分、时、日、月、周几、年(可省略)。值可以是数字,也可以是以下符号:
*:所有值都匹配
?:无所谓,不关心,通常放在“周几”里
,:或者
/:增量值
-:区间

例如:

0 * * * * *:每分钟(当秒为0的时候)
0 0 * * * *:每小时(当秒和分都为0的时候)
*/10 * * * * *:每10秒
0 5/15 * * * *:每小时的5分、20分、35分、50分
0 0 9,13 * * *:每天的9点和13点

4、@Scheduled注解的另外两个属性:fixedRate和fixedDelay

 1)fixedDelay设置的是:上一个任务结束后多久执行下一个任务;

 2)fixedRate设置的是:上一个任务的开始到下一个任务开始时间的间隔;

注:如果是强调任务间隔的定时任务,建议使用fixedRate和fixedDelay,如果是强调任务在某时某分某刻执行的定时任务,建议使用cron表达式。

5、并发执行

1)spring schedule的定时任务默认是单线程的

处理方式是等待上一个任务执行完成后,再去执行下一个任务。(无论是cron还是fixedDelay、fixedRate都遵循这个原则)。下面举一些例子:

示例1:通过cron定时执行:任务运行时间6s、每5s运行一次任务

//使用注解方式创建定时任务
<task:annotation-driven/>

@Component
public class testTask {
    private Logger logger = LoggerFactory.getLogger(testTask.class);
    @Scheduled(cron = "0/5 * * * * ?")
    public void doTask() {
        logger.info(Thread.currentThread().getName()+"===task run");
        Thread.sleep(6*1_000);
        logger.info(Thread.currentThread().getName()+"===task end");
    }
}

//日志如下
2018-06-11 16:03:00.006 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:03:06.013 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 16:03:10.115 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:03:17.267 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 16:03:20.055 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:03:26.164 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end

根据日志可以看出,spring schedule默认是单线程处理的,下一个任务会等上一个运行完再执行。

示例2:换成fixedDelay

@Scheduled(fixedDelay = 5*1_000)
public void doTask() throws InterruptedException {
    logger.info(Thread.currentThread().getName()+"===task run");
    Thread.sleep(6*1_000);
    logger.info(Thread.currentThread().getName()+"===task end");
}
//日志如下:
2018-06-11 16:31:08.122 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:31:14.139 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 16:31:19.149 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:31:25.261 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 16:31:30.269 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:31:36.385 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end

从日志可以看出,任务结束时间再经过5s开始再次运行。

示例3:fixedRate

@Scheduled(fixedRate = 5*1_000)
public void doTask() throws InterruptedException {
        logger.info(Thread.currentThread().getName()+"===task run");
        Thread.sleep(6*1_000);
        logger.info(Thread.currentThread().getName()+"===task end");
}
//日志
2018-06-11 16:54:36.118 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:54:42.580 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 16:54:42.607 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:54:48.632 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 16:54:48.639 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 16:54:55.188 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end

从日志可以看出,上一个任务结束后,下一个任务立刻开始执行了,因为:fixedRate设置的上一个任务的开始时间到下一个任务开始时间的间隔。

示例4:fixedRate

上面例子,运行时间从6s改成2s,日志如下:

2018-06-11 17:08:43.086 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 17:08:45.093 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 17:08:48.025 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 17:08:50.083 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end
2018-06-11 17:08:53.239 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task run
2018-06-11 17:08:55.245 [pool-12-thread-1] INFO  service.task.testTask -pool-12-thread-1===task end

结果和我们推断的一致,两个任务的开始时间间隔是5s。

2)配置并行处理

上面的例子是同一个task,如果前一个还没跑完后面一个就不会触发,这没有太大问题。但是,假设系统中有多个task,默认spring schedule是单线程的,就会造成不同的task也不能同时运行,就不太合理了。解决方法:

方法一:配置多个线程,但这样会导致同一个task前一个还没跑完后面又被触发的问题。

<task:scheduler id="scheduler" pool-size="2" />

方法二:让任务分别运行在不同的scheduler里

<task:scheduler id="myScheduler1"/>
<task:scheduler id="myScheduler2"/>
<task:scheduled-tasks scheduler="myScheduler1">
    <task:scheduled ref="doSomethingTask" method="doSomething" cron="${0 * * * * *}"/>
</task:scheduled-tasks>
<task:scheduled-tasks scheduler="myScheduler2">
    <task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${0 * * * * *}"/>
</task:scheduled-tasks>

spring-quartz

maven中需要引入:quartz.jar、spring-context-support.jar。

示例:

1)定义任务类:(spring集成的quartz不需要集成任何类)

@Service
public class QuartzTest {

    public void test(){
        System.out.println("It's time to run :" + new Date().toString());
        //TODO 执行任务逻辑
        //........
    }
}

2)spring-quartz.xml配置:

a)SimpleTrigger方式:

<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<list>
			<ref local="quartzTestTrigger" />
		</list>
	</property>
</bean>
<bean id="quartzTestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
	<property name="jobDetail" ref="quartzTestJob"/>
	<!-- 20秒后运行 -->
	<property name="startDelay" value="20000" />
	<!-- 每隔三十秒重复 -->
	<property name="repeatInterval" value="30000" />
</bean>

<bean id="quartzTestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="targetObject" ref="quartzTest"></property>
	<property name="targetMethod" value="autoRun"></property>
	<property name="concurrent" value="false"></property><!--不并发运行-->
</bean>

b)CronTrigger方式:

<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<list>
			<ref local="quartzTestTrigger" />
		</list>
	</property>
	<property name="startupDelay" value="500"/>
</bean>
<bean id="quartzTestTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
	<property name="jobDetail" ref="quartzTestJob"/>
	<property name="cronExpression">
		<value>0 */1 * * * ?</value>
	</property>
</bean>

<bean id="quartzTestJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="targetObject" ref="quartzTest"></property>
	<property name="targetMethod" value="autoRun"></property>
	<property name="concurrent" value="false"></property><!--不并发运行-->
</bean>

最后,在spring主配置文件application.xml中把上面的application-quartz.xml 包含进去即可。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liuxiao723846/article/details/90546619

智能推荐

windows下的安装与使用curl实现命令行访问Web网站_curl访问网站-程序员宅基地

在Windows下使用curl访问Web链接curl是什么?curl的写法应该是cURL,CommandLine Uniform Resource Locator。从名字可以看出,其是使用命令行访问URL的工具。除了HTTP协议外, 其还支持HTTPS、FTP、Telnet、LDAP等其他协议, 支持文件上传和下载等功能。Linux自带curl工具, 直接在命令行输入:curl URL地址..._curl访问网站

Android crash 流程详解(二):NE_android crash流程-程序员宅基地

在上一篇博文剖析了java layer exception 的处理流程,本文将继续剖析 Android crash 的另一部分,即 native 端的 crash,又称 native layer exception (NE。_android crash流程

s3c2410上搭建QT/Embedded4.8.5开发环境(四)--安装intel-x86 X11平台qt库qt-everywhere-opensource-src-4.8.5-程序员宅基地

【目标板】s3c-2410【虚拟机】CentOS6.4 kernel-2.6.32【编译器】arm-linux-gcc 4.3.2目前QT官网的4.8.5的几个版本中Qt libraries 4.8.5 for Linux/X11 (230 MB) (Info)和Qt libraries 4.8.5 for embedded Linux (230 MB) (Info)两个版本其实是同一

在Web应用中动态创建PDF文件-程序员宅基地

在Web应用中动态创建PDF文件gagaghost 翻译 (参与分:104,专家分:230) 发表:2004-9-15 下午6:33 版本:1.0 阅读:885次在Web应用中动态创建PDF文件原文:SeanC.Sullivan翻译:gagaghost在一个最近的后勤项目中,客户要求我们建一个能让用户能从...

stm32f103c8t6与stm32f103zet6 基于SX1276串口通信-----发送端(二)-程序员宅基地

矩阵按键控制代码/************************************************************正面朝上黑引脚为行,白引脚为列 A、B 、C 、D 分别返回 10 11 12 13*为 14#为 150为 16无按键按下返回0*************************************************************/#include "key.h"#include "delay.h"//矩阵键盘引脚初始化

分布式任务调度平台XXL-JOB安装部署_xxl-job部署_菜鸟老胡~的博客-程序员宅基地

啥是XXL_JOB?XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司(目前官网主动上报的446家)线上产品线,开箱即用。不多介绍了,直接上部署教程:一.下载文档地址中文文档 English Documentation源码仓库地址源码仓库地址 Release Download GitHub - xuxueli/xxl-job: A distributed task scheduling framewo_xxl-job部署

随便推点

黑马程序员_JAVA- System、Runtime、Math、Date、Calendar-程序员宅基地

一、System在API中--java.lang.System:属性和行为都是静态的long currentTimeMillis(); //返回当前时间毫秒值exit(); //退出虚拟机Properties getProperties() ; //获取当前系统的属性信息Properties prop = System.getProperties();//获取系统的属性信息

Fast rcnn cpu 训练自己的数据_将数据提取到cpu训练的方法是-程序员宅基地

本文介绍如何在 cpu 模式下使用 Faster RCNN demo,以及在cpu 模式下训练自己的数据。Install Faster-rcnn源码地址:https://github.com/rbgirshick/py-faster-rcnn由于 faster rcnn 依赖是基于 caffe 的,所以需要先安装 caffe,所以前提是你已经在本机上装过 caffe ,然后直接复制该 Make_将数据提取到cpu训练的方法是

如何进入嵌入式开发行业-程序员宅基地

嵌入式系统无疑是当前最热门最有发展前途的IT应用领域之一。嵌入式系统用在一些特定专用设备上, 通常这些设备的硬件资源(如处理器、存储器等)非常有限,并且对成本很敏感,有时对实时响应要求很高等。特别是随着消费家电的智能化,嵌入式更显重要。像 我们平常常见到的手机、PDA、电子字典、可视电话、 VCD/DVD/MP3 Player、数字相机(DC)、数字摄像机(DV)、U-Disk、机顶盒(Set

生成G.fst步骤报错:\data\ section missing or empty._warning (arpa2fst[5.5.0~1547-71f3]:read():arpa-fil-程序员宅基地

Training an ARPA model with SRILMTraining a model with the SRI Language Modeling Toolkit (SRILM) is easy. That’s why we recommend it. Moreover, SRILM is the most advanced toolkit up to date. To trai..._warning (arpa2fst[5.5.0~1547-71f3]:read():arpa-file-parser.cc:219) line 4041

【JVM】为什么volatile有可见性,有序性,但没有原子性_volaltile修饰的变量具有原子性吗-程序员宅基地

可见性当一个变量被定义为volatile,它将具备两个特性,第一个是保证变量对所有线程具有“可见性”,具体来说就是当一个线程对该变量进行了修改,那么别的线程会立即得知(这个变化)。那么可见性的实现原理是什么呢?实现原理:如果使用这个修饰符,对该变量进行写操作之后,会立即执行store和write操作(对应的汇编代码中会加上一个lock前缀),立即将该变量从工作内存(或者说缓存)写入主内存,保证了对别的线程立即可见(因为这会导致别的线程的工作内存中该变量的缓存会失效),并且同时其他的cpu的工作内_volaltile修饰的变量具有原子性吗

抽屉原理&&容斥原理&&欧拉函数 (小总结)-程序员宅基地

前一段时间学过了抽屉,容斥原理,还有欧拉函数,今天抽时间来总结一下首先是抽屉原理,(又称鸽巢原理)其意义是,将n个物件放回n-1个抽屉里,那么必定会有一个抽屉装有两个物件;原理很容易理解;在我学的过程中他的一个重要应用就是在n个数中总能找到一个或多个的数的和是m的倍数(当然n>=m-1),我们可以这样来分情况:1. :n个数中存在有前缀和是m的倍数;2. :