一. 定时任务实现方式

定时任务实现方式:

  • Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少,这篇文章将不做详细介绍。
  • 使用Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂,有空介绍。
  • 使用Spring的@Scheduled注解配合@EnableScheduling一起使用。
  • SpringBoot自带的Scheduled,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多,本文主要介绍。

说明:@Scheduled 注解用于标注这个方法是一个定时任务的方法,有多种配置可选。cron支持cron表达式,指定任务在特定时间执行;fixedRate以特定频率执行任务;fixedRateString以string的形式配置执行频率。

定时任务执行方式:

  • 单线程(串行)
  • 多线程(并行)

二. 创建定时任务

2.1、Spring boot中串行调度

在Spring boot中自带了Seheduled,实现起来很方便,只需要在需要调度的方法上增加注解即可。如下:

package com.dxz.demo.schedule;
import java.time.LocalDateTime; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; @Service
//@EnableScheduling 可以省略
public class PrintJob { @Scheduled(initialDelay=3000, fixedDelay = 10000)
public void print() {
Thread current = Thread.currentThread();
System.out.println(LocalDateTime.now() + " thread-name:" + current.getName() + ": 60 print");
}
}

结果:

2.2、Spring boot中并行调度

继承SchedulingConfigurer类并重写其方法即可,如下:

package com.dxz.demo.schedule;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; @Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer { @Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
} @Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
}

结果:

多个线程依次按照固定频率来执行调度的。

2.3、Spring boot中异步并行调度

2.4、Spring并行调度

1.新建一个web工程,引入spring相关包,pom文件如下

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dxz.demo</groupId>
<artifactId>SpringDemo</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringDemo Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>SpringDemo</finalName>
</build>
</project>

spring配置文件

<?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"
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.xsd"> <import resource="applicationContext-task.xml" />
<context:component-scan base-package="com.dxz.demo" />
</beans> <?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd"> <task:annotation-driven executor="taskExecutor"
scheduler="taskScheduler" />
<task:executor id="taskExecutor" pool-size="5" />
<task:scheduler id="taskScheduler" pool-size="10" />
</beans>

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>appversion</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> </web-app>

调度任务类

package com.dxz.demo;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; @Component
public class HelloWorldTask {
@Scheduled(fixedRate = 5000)
public void doSomething() throws Exception {
System.out.println(Thread.currentThread().getName() + " doSomething is invoked!");
} @Scheduled(fixedDelay = 5000)
public void doSomething2() throws Exception {
System.out.println(Thread.currentThread().getName() + " doSomething2 is invoked!");
}
}

工程结构如下

结果:

从上图中可以看到时多个线程在执行调度任务的。

三. @Scheduled参数说明:

initial-delay : 表示第一次运行前需要延迟的时间,单位是毫秒
fixed-delay : 表示从上一个任务完成到下一个任务开始的间隔, 单位是毫秒。
fixed-rate : 表示从上一个任务开始到下一个任务开始的间隔, 单位是毫秒。(如果上一个任务执行超时,则可能是上一个任务执行完成后立即启动下一个任务)
cron : cron 表达式。(定时执行,如果上一次任务执行超时而导致某个定时间隔不能执行,则会顺延下一个定时间隔时间。下一个任务和上一个任务的间隔时间不固定)
区别见图:

4、局限性——@Scheduled的cron无法指定执行的年份

  即我们假如使用下面的定时任务

    @Scheduled(cron = "0 18 10 * * ? 2016-2016")
public void testTaskWithDate() {
logger.info("测试2016.定时任务");
}

  将会报下面的错误

Cron expression must consist of 6 fields (found 7 in "0 18 10 * * ? 2016-2016")
Caused by: java.lang.IllegalStateException: Encountered invalid @Scheduled method 'testTaskWithDate': Cron expression must consist of 6 fields (found 7 in "0 18 10 * * ? 2016-2016")
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.processScheduled(ScheduledAnnotationBeanPostProcessor.java:405)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization(ScheduledAnnotationBeanPostProcessor.java:258)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)

错误原因:

/**
* Parse the given pattern expression.
*/
private void parse(String expression) throws IllegalArgumentException {
String[] fields = StringUtils.tokenizeToStringArray(expression, " ");
if (fields.length != 6) {
throw new IllegalArgumentException(String.format(""
+ "cron expression must consist of 6 fields (found %d in %s)", fields.length, expression));
}

  spring taks 不支持年位定时,它毕竟不是quartz,只是简单的定时框架,比起jdk Timer就加入了线程池而以.

  但是制定到年份,会存在一个问题,就是在你在过了这个时间后再启动项目的时候,他会一直报一个memory leak的错误,大概的意思是你的定时任务将永远不会被执行,导致项目一直启动不了。

源码里的注释:

 *The pattern is a list of six single space-separated fields: representing
 * second, minute, hour, day, month, weekday. Month and weekday names can be
 * given as the first three letters of the English names.

四、Cron表达式的详细用法

字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
例子:
0/5 * * * * ? : 每5秒执行一次
“*”字符被用来指定所有的值。如:"*"在分钟的字段域里表示“每分钟”。
“?”字符只在日期域和星期域中使用。它被用来指定“非明确的值”。当你需要通过在这两个域中的一个来指定一些东西的时候,它是有用的。看下面的例子你就会明白。
月份中的日期和星期中的日期这两个元素时互斥的一起应该通过设置一个问号来表明不想设置那个字段。
“-”字符被用来指定一个范围。如:“10-12”在小时域意味着“10点、11点、12点”。
“,”字符被用来指定另外的值。如:“MON,WED,FRI”在星期域里表示”星期一、星期三、星期五”。
“/”字符用于指定增量。如:“0/15”在秒域意思是每分钟的0,15,30和45秒。“5/15”在分钟域表示每小时的5,20,35和50。 符号“*”在“/”前面(如:*/10)等价于0在“/”前面(如:0/10)。记住一条本质:表达式的每个数值域都是一个有最大值和最小值的集合,如: 秒域和分钟域的集合是0-59,日期域是1-31,月份域是1-12。字符“/”可以帮助你在每个字符域中取相应的数值。如:“7/6”在月份域的时候只 有当7月的时候才会触发,并不是表示每个6月。
L是‘last’的省略写法可以表示day-of-month和day-of-week域,但在两个字段中的意思不同,例如day-of- month域中表示一个月的最后一天。如果在day-of-week域表示‘7’或者‘SAT’,如果在day-of-week域中前面加上数字,它表示 一个月的最后几天,例如‘6L’就表示一个月的最后一个星期五。
字符“W”只允许日期域出现。这个字符用于指定日期的最近工作日。例如:如果你在日期域中写 “15W”,表示:这个月15号最近的工作日。所以,如果15号是周六,则任务会在14号触发。如果15好是周日,则任务会在周一也就是16号触发。如果 是在日期域填写“1W”即使1号是周六,那么任务也只会在下周一,也就是3号触发,“W”字符指定的最近工作日是不能够跨月份的。字符“W”只能配合一个 单独的数值使用,不能够是一个数字段,如:1-15W是错误的。
“L”和“W”可以在日期域中联合使用,LW表示这个月最后一周的工作日。
字符“#”只允许在星期域中出现。这个字符用于指定本月的某某天。例如:“6#3”表示本月第三周的星期五(6表示星期五,3表示第三周)。“2#1”表示本月第一周的星期一。“4#5”表示第五周的星期三。
字符“C”允许在日期域和星期域出现。这个字符依靠一个指定的“日历”。也就是说这个表达式的值依赖于相关的“日历”的计算结果,如果没有“日历” 关联,则等价于所有包含的“日历”。如:日期域是“5C”表示关联“日历”中第一天,或者这个月开始的第一天的后5天。星期域是“1C”表示关联“日历” 中第一天,或者星期的第一天的后1天,也就是周日的后一天(周一)。

五、表达式举例

"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

常用示例:

0 0 12 * * ? 每天12点触发
0 15 10 ? * * 每天10点15分触发
0 15 10 * * ? 每天10点15分触发
0 15 10 * * ? * 每天10点15分触发
0 15 10 * * ? 2005 2005年每天10点15分触发
0 * 14 * * ? 每天下午的 2点到2点59分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 2点到2点59分、18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发
0 15 10 15 * ? 每月15号上午10点15分触发
0 15 10 L * ? 每月最后一天的10点15分触发
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)

参考:http://wujq4java.iteye.com/blog/2067214

参考:http://blog.csdn.net/applebomb/article/details/52400154

Spring3.0.6定时任务的更多相关文章

  1. spring3.0注解定时任务配置及说明

    spring注解方式的定时任务配置: 第一步:spring配置文件 <?xml version="1.0" encoding="UTF-8"?> & ...

  2. Spring3.0.6定时任务task:scheduled

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  3. 模块化之Spring3.0 web fragment和gradle构建项目

      1.背景 模块化开发很久以前就开始普及的概念.但是到了企业实际情况中,真正把模块化作为系统架构的核心的不多.或者说对模块化有这个意识,但是具体到底该如何实现,有些模糊,同时也许因为项目紧.任务中. ...

  4. spring3.0使用annotation完全代替XML(三)

    很久之前写过两篇博客: spring3.0使用annotation完全代替XML spring3.0使用annotation完全代替XML(续) 用java config来代替XML,当时还遗留下一些 ...

  5. spring3.0使用annotation完全代替XML

    @Service与@Component有什么不同?那天被问到这个问题,一时之间却想不起来,就利用这篇文章来纪录spring3.0中常用的annotation. 从spring2.5开始,annotat ...

  6. Spring3.0 与 MyBatis框架 整合小实例

    本文将在Eclipse开发环境下,采用Spring MVC + Spring + MyBatis + Maven + Log4J 框架搭建一个Java web 项目. 1. 环境准备: 1.1 创建数 ...

  7. Spring3.0之后->Spring MVC过滤器-HiddenHttpMethodFilter

    浏览器form表单只支持GET与POST请求,而DELETE.PUT等method并不支持,spring3.0添加了一个过滤器,可以将这些请求转换为标准的http方法,使得支持GET.POST.PUT ...

  8. spring3.0+Atomikos 构建jta的分布式事务 -- NO

    摘自: http://gongjiayun.iteye.com/blog/1570111 spring3.0+Atomikos 构建jta的分布式事务 spring3.0已经不再支持jtom了,不过我 ...

  9. Jbpm4.4+hibernate3.5.4+spring3.0.4+struts2.1.8整合例子(附完整的请假流程例子,jbpm基础,常见问题解决)

    Jbpm4.4+hibernate3.5.4+spring3.0.4+struts2.1.8 整合例子(附完整的请假流程例子). 1.jbpm4.4 测试环境搭建 2.Jbpm4.4+hibernat ...

随机推荐

  1. 本地替换文件读取MYSQL密码

    Mysql 的密码默认是存储在/data/mysql/下面的三个文件中:user.MYD,user.frm,user.MYI 先把这三个文件下载到本地,然后替换本地的这三个文件 使用net stop ...

  2. x11vnc

    http://cisight.com/how-to-setup-vnc-server-remote-desktop-in-ubuntu-11-10-oneiric/ Install VNC serve ...

  3. VS2013编译OpenSSL

    简述 OpenSSL是一个开源的第三方库,它实现了SSL(Secure SocketLayer)和TLS(Transport Layer Security)协议,被广泛企业应用所采用.对于一般的开发人 ...

  4. 美丽渐变色的Form

    一直都非常喜欢渐变色的界面,但是没想到漂亮的渐变Form原来这么简单...实在是没想到...看来我不仅技术水平低,脑袋里的创意也是空空如也... --------------------------- ...

  5. Redis的Set操作

    集合的性质: 唯一性,无序性,确定性 注: 在string和link的命令中,可以通过range 来访问string中的某几个字符或某几个元素 但,因为集合的无序性,无法通过下标或范围来访问部分元素. ...

  6. RichLabel基于Cocos2dx+Lua v3.x

    RichLabel 简介 RichLabel基于Cocos2dx+Lua v3.x解析字符串方面使用了labelparser,它可以将一定格式的字符串,转换为lua中的表结构扩展标签极其简单,只需添加 ...

  7. wiremock之录制和回放

    Recording is done by starting the standalone runner like this: $ java -jar wiremock-1.50-standalone. ...

  8. PHP,单项查询及多项查询

    先封装对象class DBDA { public $host = "localhost"; //数据库地址 public $uid = "root"; //数据 ...

  9. Mac 版 QQ 可直接访问 iPhone 的相册 ?!

    在QQ的聊天窗口中,点击 发送图片 的按钮,会有两个选择项,其中一个就是 从iPhone相册中选取 ,如图 点击  从iPhone相册中选取 后,iPhone上的QQ会收到一条消息 “请选择要上传的照 ...

  10. jpa+spring配置多数据源

    property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test?useU ...