Quartz定时器与定时任务知识概括
定时任务调度
什么是定时任务:
- 定时任务是指调度程序在指定的时间或周期触发执行的任务
- 使用场景:发送邮件、统计、状态修改、消息推送、活动开启、增量索引
定时任务实现技术:
- Java自带的
java.util.Timer
类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。使用较少。 ScheduledExecutorService:
也是jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。- Spring3.0以后自主开发的定时任务工具
spring task
,使用简单,支持线程池,可以高效处理许多不同的定时任务,除spring相关的包外不需要额外的包,支持注解和配置文件两种形式。不能处理过于复杂的任务 - 专业的定时框架
quartz
,功能强大,可以让你的程序在指定时间执行,也可以按照某一个频度执行,支持数据库、监听器、插件、集群。一般使用spring整合quartz或者springboot整合quartz。
其他定时器
Timer:
/**
* java timer 测试类
* Created by gaozhy on 2017/6/24.
*/
public class JavaTimer {
public static void main(String[] args) {
try {
// 创建定时器
Timer timer = new Timer();
// 添加调度任务
// 安排指定的任务在指定的时间开始进行重复的 固定延迟执行
timer.schedule(new MyTask(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2017-06-24 22:31:10"),10*1000);
// 安排指定的任务在指定的延迟后开始进行重复的 固定速率执行
//timer.scheduleAtFixedRate(new MyTask(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2017-06-24 22:31:10"),10*1000);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
/**
* 任务类
* Created by gaozhy on 2017/6/24.
*/
public class MyTask extends TimerTask{
// 定义调度任务
public void run() {
System.out.println("log2:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
ScheduledExecutorService:
- 该方法跟Timer类似,直接看demo:
public class TestScheduledExecutorService {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
// 参数:1、任务体 2、首次执行的延时时间
// 3、任务执行间隔 4、间隔时间单位
service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
}
}
Spring Task:
- 基于配置:
/**
* Spring Task 任务类
* Created by gaozhy on 2017/6/24.
*/
public class SpringTask {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void m1(){
System.out.println("m1:"+simpleDateFormat.format(new Date()));
}
public void m2(){
System.out.println("m2:"+simpleDateFormat.format(new Date()));
}
public void m3(){
System.out.println("m2:"+simpleDateFormat.format(new Date()));
}
}
<!--spring-task.xml配置-->
<bean id="springTask" class="com.baizhi.task.SpringTask"></bean>
<!--注册调度任务-->
<task:scheduled-tasks>
<!--延迟8秒 执行任务-->
<!--<task:scheduled ref="springTask" method="m1" fixed-delay="8000" />-->
<!--固定速度5秒 执行任务-->
<!--<task:scheduled ref="springTask" method="m2" fixed-rate="5000"/>-->
<!--
使用cron表达式 指定触发时间
spring task 只支持6位的cron表达式 秒 分 时 日 月 星期
-->
<task:scheduled ref="springTask" method="m3" cron="50-59 * * ? * *"/>
</task:scheduled-tasks>
<!--执行器配置-->
<task:executor id="threadPoolTaskExecutor" pool-size="10" keep-alive="5"></task:executor>
<!--调度器配置-->
<task:scheduler id="threadPoolTaskScheduler" pool-size="10"></task:scheduler>
- 基于注解
/**
* Spring Task 任务类
* Created by gaozhy on 2017/6/24.
*/
public class SpringAnnotationTask {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/* @Scheduled(fixedDelay = 8000)
public void m1(){
System.out.println("m1:"+simpleDateFormat.format(new Date()));
}
@Scheduled(fixedRateString = "5000")
public void m2(){
System.out.println("m2:"+simpleDateFormat.format(new Date()));
}
*/
@Scheduled(cron = "0-30 * * * * ?")
public void m3(){
System.out.println("m2:"+simpleDateFormat.format(new Date()));
}
}
<!--开启基于注解的spring task-->
<task:annotation-driven></task:annotation-driven>
<bean id="springAnnotationTask" class="com.baizhi.task.SpringAnnotationTask"></bean>
Spring Task任务执行和调度(TaskExecutor,TaskSchedule):
介绍:
①Spring Framework分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。
Spring还具有支持线程池或在应用程序服务器环境中委托给CommonJ的接口的实现。最终,在公共接口背后使用这些实现抽象出了JavaSE 5,Java SE 6和Java EE环境之间的差异。
②Spring还提供了集成类,用于支持使用Timer(自1.3以来的JDK的一部分)和Quartz Scheduler(http://quartz-scheduler.org)进行调度。这两个调度程序都是使用FactoryBean设置的,它们分别具有对Timer或Trigger实例的可选引用。此外,还提供了Quartz Scheduler和Timer的便捷类,它允许您调用现有目标对象的方法(类似于正常的MethodInvokingFactoryBean操作)。Spring官方对TaskExecutor的相关解释:
①Spring的TaskExecutor接口与java.util.concurrent.Executor接口相同
。该接口具有单个方法(execute(Runnable task)),该方法根据线程池的语义和配置接受要执行的任务。
②TaskExecutor接口相关实现类:
实现类名 | 对应解释(直接甩翻译了) |
---|---|
SyncTaskExecutor | 该实现类不会执行异步调用。 相反,每次调用都在调用的线程中进行(翻译过来也即同步任务执行器)。 它主要用于不需要多线程的情况,例如在简单的测试用例中。 |
SimpleAsyncTaskExecutor | 此实现不会重用任何线程。 相反,它为每次调用启动一个新线程。 但是,它确实支持并发限制,该限制会阻止超出限制的任何调用,直到释放插槽为止。(说简单了,就是要使用了直接创建一个线程) |
ConcurrentTaskExecutor | 此实现是java.util.concurrent.Executor实例的适配器。很少需要直接使用ConcurrentTaskExecutor**(官网自己都觉得很少使用,不过相对于ThreadPoolTaskExecutor,官网推荐如果ThreadPoolTaskExecutor不够灵活,无法满足需求,则可以使用ConcurrentTaskExecutor)**。 |
ThreadPoolTaskExecutor | 杀手锏级的任务调度器(最常用),可以说已经足够满足我们的需求了(除非,非常非常特例才使用ConcurrentTaskExecutor)。官网翻译重要片段:公开了bean属性,用于配置java.util.concurrent.ThreadP |
WorkManagerTaskExecutor | 此实现使用CommonJ WorkManager作为其后备服务提供程序,并且是在Spring应用程序上下文中在WebLogic或WebSphere上设置基于CommonJ的线程池集成的中心便利类。 |
DefaultManagedTaskExecutor | 此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用JNDI获取的ManagedExecutorService,为此目的替换CommonJ WorkManager。(说明了就是依赖环境) |
Spring官方对TaskSheduler接口的相关解释:
①用于在将来的某个时间点调度任务。
②TaskScheduler接口相关实现类:
实现类名 | 对应解释 |
---|---|
ConcurrentTaskScheduler | 该类实际继承了ConcurrentTaskExecutor对象。只是实现了TaskScheduler接口,增加了相关定时调度任务的方法。 |
ThreadPoolTaskScheduler | spring对该类设计原则同ThreadPoolTaskExecutor类。是为了定时调度任务不依赖相关的运行容器(例如weblogic、WebSphere等)。其底层委托给ScheduledExecutorService,向外暴露相关的常见bean配置属性。 |
- 总结:
①链接:
<1>spring任务执行器与任务调度器
<2>JAVA定时任务Timer、Spring Task、Quartz
②Quartz与Spring Task对比:
<1>Quartz:
1、默认多线程异步执行
2、单个任务时,在上一个调度未完成时,下一个调度时间到时,会另起一个线程开始新的调度。业务繁忙时,一个任务会有多个调度,可能导致数据处理异常。
3、多个任务时,任务之间没有直接影响,多任务执行的快慢取决于CPU的性能
4、触发方式 : (1)SimpleTrigger (2)CronTrigger
5、能被集群实例化,支持分布式部署
6、使用JobStoreCMT(JDBCJobStore的子类),Quartz 能参与JTA事务;Quartz 能管理JTA事务(开始和提交)在执行任务之间,这样,任务做的事就可以发生在JTA事务里。
<2>Task:
1、默认单线程同步执行
2、单个任务时,当前次的调度完成后,再执行下一次任务调度
3、多个任务时,一个任务执行完成后才会执行下一个任务。若需要任务能够并发执行,需手动设置线程
4、触发方式:与Quartz的CronTrigger的表达式类似,可以使用注解标注定时任务。
Quartz简介
Quartz简介:
- Quartz是OpenSymphony开源组织在Jobscheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。
- Quartz用一个小Java库发布文件(.jar文件),这个库文件包含了所有Quartz核心功能。这些功能的主要接口(API)是Scheduler接口。它提供了简单的操作,例如:将任务纳入日程或者从日程中取消,开始/停止/暂停日程进度。
springboot定时任务(与Quartz整合):
- 直接加入集成好的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
定时器种类:
- Quartz 中五种类型的Trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,NthIncludedDayTrigger和Calendar类( org.quartz.Calendar)。
- 最常用的:
①SimpleTrigger:用来触发只需执行一次或者在给定时间触发并且重复N次且每次执行延迟一定时间的任务。
②CronTrigger:按照日历触发,例如“每个周五”,每个月10日中午或者10:15分。
存储方式:
- RAMJobStore和JDBCJobStore对比:
类型 | 优点 | 缺点 |
---|---|---|
RAMJobStore | 不要外部数据库,配置容易,运行速度快 | 因为调度程序信息是存储在被分配给JVM的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个Job和Trigger将会受到限制 |
JDBCJobStore | 支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务 | 运行速度的快慢取决与连接数据库的快慢 |
表关系和解释:
- 表关系:
- 解释:
表名称 | 说明 |
---|---|
qrtz_blob_triggers | Trigger作为Blob类型存储(用于Quartz用户用JDBC创建他们自己定制的Trigger类型,JobStore 并不知道如何存储实例的时候) |
qrtz_calendars | 以Blob类型存储Quartz的Calendar日历信息, quartz可配置一个日历来指定一个时间范围 |
qrtz_cron_triggers | 存储Cron Trigger,包括Cron表达式和时区信息。 |
qrtz_fired_triggers | 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 |
qrtz_job_details | 存储每一个已配置的Job的详细信息 |
qrtz_locks | 存储程序的非观锁的信息(假如使用了悲观锁) |
qrtz_paused_trigger_graps | 存储已暂停的Trigger组的信息 |
qrtz_scheduler_state | 存储少量的有关 Scheduler的状态信息,和别的 Scheduler 实例(假如是用于一个集群中) |
qrtz_simple_triggers | 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数 |
qrtz_triggers | 存储已配置的 Trigger的信息 |
核心类和关系:
- 核心类:
①QuartzSchedulerThread:
负责执行向QuartzScheduler注册的触发Trigger的工作的线程。
②ThreadPool:
Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提供运