java定时任务实现的几种方式(Timer、Spring Task、Quartz)
Timer
JDK自带的Timer类,允许调度一个TimerTask任务。
Demo:
/**
* Timer测试类
*/
public class TimerDemo {
public static void main(String[] args) {
// 创建定时器
Timer timer = new Timer();
// 添加调度任务
// schedule(TimerTask task, Date time); 特定时间 time 执行
// timer.schedule(new MyTask(), new Date(System.currentTimeMillis() + 1000));
// schedule(TimerTask task, long delay); //延迟 delay毫秒 执行 task
// timer.schedule(new MyTask(), 1000);
// schedule(TimerTask task, long delay, long period) 延迟 delay毫秒 执行并每隔 period毫秒 执行一次
// timer.schedule(new MyTask(), 1000, 5000);
// schedule(TimerTask task, Date time, long period); 特定时间 time 执行并每隔 period毫秒 执行一次
timer.schedule(new MyTask(), new Date(System.currentTimeMillis() + 1000), 5000);
}
}
/**
* 具体执行的任务
*/
public class MyTask extends TimerTask {
/**
* The action to be performed by this timer task.
*/
public void run() {
System.out.println("执行时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
Spring Task:
Spring3.0以后自主开发的定时任务工具Spring Task,支持线程池,可以高效处理许多不同的定时任务,除spring相关的包外不需要额外的包,支持注解和配置文件两种形式。 但不能处理过于复杂的任务 。
基于配置的Demo:
定时任务类:
/**
* spring执行任务的类
*/
public class SpringTask {
public void show1() {
System.out.println("show1:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
public void show2() {
System.out.println("show2:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
配置文件spring-schedule.xml:
<?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.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<bean id="springTask" class="com.zy.springtask.SpringTask"></bean>
<!--注册调度任务-->
<task:scheduled-tasks>
<!--延迟1秒 执行任务-->
<!--<task:scheduled ref="springTask" method="show1" fixed-delay="1000" />-->
<!--固定速度3秒 执行任务-->
<!--<task:scheduled ref="springTask" method="show2" fixed-rate="3000" />-->
<!--
使用cron表达式 指定触发时间
spring task 只支持6位的cron表达式 秒 分 时 日 月 星期
-->
<task:scheduled ref="springTask" method="show1" cron="1-10 * * ? * *" />
</task:scheduled-tasks>
<!--执行器配置-->
<task:executor id="threadPoolTaskExecutor" pool-size="10" keep-alive="5"></task:executor>
<!--调度器配置-->
<task:scheduler id="threadPoolTaskScheduler" pool-size="10"></task:scheduler>
</beans>
基于注解的Demo:
定时任务类:
/**
* spring执行任务的类
*/
@Component
public class SpringAnnoTask {
@Scheduled(cron = "1-10 * * * * ? ")//每分钟的1-10秒每秒执行一次
public void show1() {
System.out.println("show1:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
@Scheduled(cron = "0/10 * * * * ? ")//每10秒执行一次
public void show2() {
System.out.println("show2:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
配置文件spring-schedule.xml:
<?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.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!--启用注解-->
<task:annotation-driven></task:annotation-driven>
<bean id="springAnnotationTask" class="com.zy.springtask.SpringAnnoTask"></bean>
</beans>
Quartz
这个就厉害了,这篇文章只能简单介绍个入门案例,如果需要深入研究请自行查看官方文档或者网上找些系列的文章。w3cschool文档
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。
特点:
- 强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
- 灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
- 分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
- 另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。
核心元素 :
- Scheduler: 任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
- Trigger :触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
- Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
- Job :任务,是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。实现Job接口的任务,默认是无状态的,若要将Job设置成有状态的,在quartz中是给实现的Job添加@DisallowConcurrentExecution注解(以前是实现StatefulJob接口,现在已被Deprecated),在与spring结合中可以在spring配置文件的job detail中配置concurrent参数。
- JobDetail :任务信息,用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。
Trigger触发器 :
- SimpleTrigger :在一个指定时间段内执行一次作业任务或是在指定时间间隔内执行多次作业任务;
- CronTrigger :基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,比SimpleTrigger更常用。
简单案例:
jar包依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
具体定时任务需要执行的类:
/**
* 具体执行的任务 实现Job接口
*/
public class MyDemoJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 当前时间
System.out.println("执行时间:" + sf.format(new Date()));
// 获取Trigger
Trigger trigger = jobExecutionContext.getTrigger();
// 通过trigger获取job标识
JobKey jobKey = trigger.getJobKey();
System.out.println("Job's key:" + "name:" + jobKey.getName() + "\tgroup:" + jobKey.getGroup());
// getClass();
System.out.println("Start time : " + sf.format(trigger.getStartTime()));
// System.out.println("End time : " + sf.format(trigger.getEndTime()));
}
}
基础Demo:
/**
* 基础触发器调度程序
*/
public class BaseScheduler {
public static void main(String[] args) {
try {
// 1. 创建一个JodDetail实例 将该实例与具体要执行的job类 MyDemoJob.class绑定
JobDetail jobDetail = JobBuilder.newJob(MyDemoJob.class) // 定义Job类为MyDemoJob类(具体执行定时任务的内容)
.withIdentity("myJob", "default") // 定义name/group
.build();
// 2. 定义一个Trigger,10秒后执行
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
// 2.1 设置开始时间
Date startTime = new Date();
startTime.setTime(startTime.getTime() + 10000L);
// 2.2 设置结束时间
// Date endTime = new Date();
//endTime.setTime(endTime.getTime() + 20000L);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "default")// 定义名字和组
.startAt(startTime)
//.endAt(endTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
// 上面这句的意思是 这个定时器每5秒执行一次 直到山峰没有棱角 河水不再流 详细使用请查看withSchedule的参数设置
.build();
// 3. 创建scheduler 从StdSchedulerFactory工厂中获取
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 4. 将jobDetail和trigger加入这个调度(注册 任务详情和触发器)
scheduler.scheduleJob(jobDetail, trigger);
// 5. 启动scheduler
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
基于SimpleTrigger的Demo:
/**
* SimpleTrigger 简单触发器调度程序
*/
public class SimpleTriggerScheduler {
public static void main(String[] args) {
try {
// 1. 创建一个JodDetail实例
JobDetail jobDetail = JobBuilder.newJob(MyDemoJob.class)
.withIdentity("myJob")
.build();
// 2. 定义一个Trigger
Date startTime = new Date();
startTime.setTime(startTime.getTime() + 5000L);
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")// 定义名字和组
.startAt(startTime)
.build();
// 3. 创建scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 4. 将jobDetail和trigger加入这个调度(注册 任务详情和触发器)
scheduler.scheduleJob(jobDetail, trigger);
// 5. 启动scheduler
scheduler.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
基于CronTrigger的Demo:
/**
* CronTrigger 基于Cron表达式的触发器调度程序
*/
public class CronTriggerScheduler {
public static void main(String[] args) {
try {
// 1. 创建一个JodDetail实例
JobDetail jobDetail = JobBuilder.newJob(MyDemoJob.class)
.withIdentity("myJob") // 定义name/group
.build();
// 2. 定义一个Trigger 使用Cron表达式来控制运行
CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.withSchedule( //定义任务调度的时间间隔和次数
CronScheduleBuilder
.cronSchedule("0/10 * * * * ? ")//每10秒运行一次
)
.build();
// 3. 创建scheduler
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
// 4. 将jobDetail和trigger加入这个调度(注册 任务详情和触发器)
scheduler.scheduleJob(jobDetail, trigger);
// 5. 启动scheduler
scheduler.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
PS:Cron表达式介绍
(1)Seconds Minutes Hours DayofMonth Month DayofWeek Year
(2)Seconds Minutes Hours DayofMonth Month DayofWeek
各个字段的含义:
字段 | 允许值 | 允许的特殊字符 |
秒(Seconds) | 0~59的整数 | , - * / 四个字符 |
分(Minutes) | 0~59的整数 | , - * / 四个字符 |
小时(Hours) | 0~23的整数 | , - * / 四个字符 |
日期(DayofMonth) | 1~31的整数(但是你需要考虑你月的天数) | ,- * ? / L W C 八个字符 |
月份(Month) | 1~12的整数或者 JAN-DEC | , - * / 四个字符 |
星期(DayofWeek) | 1~7的整数或者 SUN-SAT (1=SUN) | , - * ? / L C # 八个字符 |
年(可选,留空)(Year) | 1970~2099 | , - * / 四个字符 |
每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:
(1)*:表示匹配该域的任意值。假如在Minutes域使用*, 即表示每分钟都会触发事件。
(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。
例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
(3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
(4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.
(5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
(6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。
例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;
如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。
(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。