java--Quartz 定时执行
第一步:引包(Maven)
<!-- 定时任务 -->
<dependency> <groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.2</version>
</dependency>
第二步:创建要被定执行的任务类
这一步也很简单,只需要创建一个实现了org.quartz.Job接口的类,并实现这个接口的唯一一个方法execute(JobExecutionContext arg0) throws JobExecutionException即可。如:
package com.fync.quartz; import java.text.SimpleDateFormat;
import java.util.Date; import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 创建要被定执行的任务类
* @author long
*
*/
public class MyJob implements Job{ public void execute(JobExecutionContext context)
throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(sdf.format(new Date()));
} }
第三步:创建任务调度,并执行
package com.fync.quartz; import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory; /**
* 创建任务调度,并执行
* @author long
*
*/
public class MainScheduler { //创建调度器
public static Scheduler getScheduler() throws SchedulerException{
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
return schedulerFactory.getScheduler();
} public static void schedulerJob() throws SchedulerException{
//创建任务
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
//创建触发器 每3秒钟执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group3")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
.build();
Scheduler scheduler = getScheduler();
//将任务及其触发器放入调度器
scheduler.scheduleJob(jobDetail, trigger);
//调度器开始调度任务
scheduler.start(); } public static void main(String[] args) throws SchedulerException {
MainScheduler mainScheduler = new MainScheduler();
mainScheduler.schedulerJob();
} }
MainScheduler.main()
依次创建了scheduler(调度器)、job(任务)、trigger(触发器),其中,job指定了MyJob,trigger保存job的触发执行策略(每隔3s执行一次),scheduler将job和trigger绑定在一起,最后scheduler.start()
启动调度,每隔3s触发执行JobImpl.execute()
,
打印出当前时间。 除了SimpleScheduler之外,常用的还有CronTrigger. 原理分析:
1、job(任务)
job由若干个class
和interface
实现。
Job接口
开发者想要job完成什么样的功能,必须且只能由开发者自己动手来编写实现,比如demo中的MyJob
,这点无容置疑。但要想让自己的job被quartz识别,就必须按照quartz的规则来办事,这个规则就是job实现类必须实现Job接口,比如MyJob就实现了Job
。
Job
只有一个execute(JobExecutionContext)
,JobExecutionContext
保存了job的上下文信息,比如绑定的是哪个trigger。job实现类必须重写execute()
,执行job实际上就是运行execute()
。
2、JobDetailImpl类 / JobDetail接口
JobDetailImpl类
实现了JobDetail接口
,用来描述一个job,定义了job所有属性及其get/set方法。了解job拥有哪些属性,就能知道quartz能提供什么样的能力,下面用表格列出job若干核心属性。
属性名 | 说明 |
---|---|
class | 必须是job实现类(比如JobImpl ),用来绑定一个具体job |
name | job名称。如果未指定,会自动分配一个唯一名称。所有job都必须拥有一个唯一name,如果两个job的name重复,则只有最前面的job能被调度 |
group | job所属的组名 |
description | job描述 |
durability | 是否持久化。如果job设置为非持久,当没有活跃的trigger与之关联的时候,job会自动从scheduler中删除。也就是说,非持久job的生命期是由trigger的存在与否决定的 |
shouldRecover | 是否可恢复。如果job设置为可恢复,一旦job执行时scheduler发生hard shutdown(比如进程崩溃或关机),当scheduler重启后,该job会被重新执行 |
jobDataMap | 除了上面常规属性外,用户可以把任意kv数据存入jobDataMap,实现job属性的无限制扩展,执行job时可以使用这些属性数据。此属性的类型是JobDataMap ,实现了Serializable接口 ,可做跨平台的序列化传输
|
3、JobBuilder类
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
上面代码是demo一个片段,可以看出JobBuilder类
的作用:接收job实现类MyJob
,生成JobDetail
实例,默认生成JobDetailImpl
实例。
这里运用了建造者模式:JobImpl
相当于Product;JobDetail
相当于Builder,拥有job的各种属性及其get/set方法;JobBuilder
相当于Director,可为一个job组装各种属性。
4、trigger(触发器)
trigger由若干个class
和interface
实现。
SimpleTriggerImpl类 / SimpleTrigger接口 / Trigger接口
SimpleTriggerImpl类
实现了SimpleTrigger接口
,SimpleTrigger接口
继承了Trigger接口
,它们表示触发器,用来保存触发job的策略,比如每隔几秒触发job。实际上,quartz有两大触发器:SimpleTrigger
和CronTrigger
,限于篇幅,本文仅介绍SimpleTrigger
。
Trigger诸类保存了trigger所有属性,同job属性一样,了解trigger属性有助于我们了解quartz能提供什么样的能力,下面用表格列出trigger若干核心属性。
在quartz源码或注释中,经常使用fire(点火)这个动词来命名属性名,表示触发job。
属性名 | 属性类型 | 说明 |
---|---|---|
name | 所有trigger通用 | trigger名称 |
group | 所有trigger通用 | trigger所属的组名 |
description | 所有trigger通用 | trigger描述 |
calendarName | 所有trigger通用 | 日历名称,指定使用哪个Calendar类,经常用来从trigger的调度计划中排除某些时间段 |
misfireInstruction | 所有trigger通用 | 错过job(未在指定时间执行的job)的处理策略,默认为MISFIRE_INSTRUCTION_SMART_POLICY 。详见这篇blog[5] |
priority | 所有trigger通用 | 优先级,默认为5 。当多个trigger同时触发job时,线程池可能不够用,此时根据优先级来决定谁先触发 |
jobDataMap | 所有trigger通用 | 同job的jobDataMap。假如job和trigger的jobDataMap有同名key,通过getMergedJobDataMap() 获取的jobDataMap,将以trigger的为准 |
startTime | 所有trigger通用 | 触发开始时间,默认为当前时间。决定什么时间开始触发job |
endTime | 所有trigger通用 | 触发结束时间。决定什么时间停止触发job |
nextFireTime | SimpleTrigger私有 | 下一次触发job的时间 |
previousFireTime | SimpleTrigger私有 | 上一次触发job的时间 |
repeatCount | SimpleTrigger私有 | 需触发的总次数 |
timesTriggered | SimpleTrigger私有 | 已经触发过的次数 |
repeatInterval | SimpleTrigger私有 | 触发间隔时间 |
5、TriggerBuilder类
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group3")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
.build();
上面代码是demo一个片段,可以看出TriggerBuilder类
的作用:生成Trigger实例,默认生成SimpleTriggerImpl
实例。同JobBuilder
一样,这里也运用了建造者模式。
6、scheduler(调度器)
scheduler主要由StdScheduler类
、Scheduler接口
、StdSchedulerFactory类
、SchedulerFactory接口
、QuartzScheduler类
实现,它们的关系见下面UML图。
// 创建调度器
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
......
// 将任务及其触发器放入调度器
scheduler.scheduleJob(jobDetail, trigger);
// 调度器开始调度任务
scheduler.start();
上面代码是demo一个片段,可以看出这里运用了工厂模式,通过factory类(StdSchedulerFactory
)生产出scheduler实例(StdScheduler
)。scheduler是整个quartz的关键,为此,笔者把demo中用到的scheduler接口的源码加上中文注释做个讲解。
- StdSchedulerFactory.getScheduler()源码
public Scheduler getScheduler() throws SchedulerException {
// 读取quartz配置文件,未指定则顺序遍历各个path下的quartz.properties文件
// 解析出quartz配置内容和环境变量,存入PropertiesParser对象
// PropertiesParser组合了Properties(继承Hashtable),定义了一系列对Properties的操作方法,比如getPropertyGroup()批量获取相同前缀的配置。配置内容和环境变量存放在Properties成员变量中
if (cfg == null) {
initialize();
}
// 获取调度器池,采用了单例模式
// 其实,调度器池的核心变量就是一个hashmap,每个元素key是scheduler名,value是scheduler实例
// getInstance()用synchronized防止并发创建
SchedulerRepository schedRep = SchedulerRepository.getInstance(); // 从调度器池中取出当前配置所用的调度器
Scheduler sched = schedRep.lookup(getSchedulerName()); ...... // 如果调度器池中没有当前配置的调度器,则实例化一个调度器,主要动作包括:
// 1)初始化threadPool(线程池):开发者可以通过org.quartz.threadPool.class配置指定使用哪个线程池类,比如SimpleThreadPool。先class load线程池类,接着动态生成线程池实例bean,然后通过反射,
// 使用setXXX()方法将以org.quartz.threadPool开头的配置内容赋值给bean成员变量;
// 2)初始化jobStore(任务存储方式):开发者可以通过org.quartz.jobStore.class配置指定使用哪个任务存储类,比如RAMJobStore。先class load任务存储类,接着动态生成实例bean,然后通过反射,
// 使用setXXX()方法将以org.quartz.jobStore开头的配置内容赋值给bean成员变量;
// 3)初始化dataSource(数据源):开发者可以通过org.quartz.dataSource配置指定数据源详情,比如哪个数据库、账号、密码等。jobStore要指定为JDBCJobStore,dataSource才会有效;
// 4)初始化其他配置:包括SchedulerPlugins、JobListeners、TriggerListeners等;
// 5)初始化threadExecutor(线程执行器):默认为DefaultThreadExecutor;
// 6)创建工作线程:根据配置创建N个工作thread,执行start()启动thread,并将N个thread顺序add进threadPool实例的空闲线程列表availWorkers中;
// 7)创建调度器线程:创建QuartzSchedulerThread实例,并通过threadExecutor.execute(实例)启动调度器线程;
// 8)创建调度器:创建StdScheduler实例,将上面所有配置和引用组合进实例中,并将实例存入调度器池中
sched = instantiate(); return sched;
}
上面有个过程是初始化jobStore,表示使用哪种方式存储scheduler相关数据。quartz有两大jobStore:RAMJobStore
和JDBCJobStore
。RAMJobStore
把数据存入内存,性能最高,配置也简单,但缺点是系统挂了难以恢复数据。JDBCJobStore
保存数据到数据库,保证数据的可恢复性,但性能较差且配置复杂。
- QuartzScheduler.scheduleJob(JobDetail, Trigger)源码
public Date scheduleJob(JobDetail jobDetail,
Trigger trigger) throws SchedulerException {
// 检查调度器是否开启,如果关闭则throw异常到上层
validateState();
......
// 获取trigger首次触发job的时间,以此时间为起点,每隔一段指定的时间触发job
Date ft = trig.computeFirstFireTime(cal); if (ft == null) {
throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
} // 把job和trigger注册进调度器的jobStore
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
// 通知job监听者
notifySchedulerListenersJobAdded(jobDetail);
// 通知调度器线程
notifySchedulerThread(trigger.getNextFireTime().getTime());
// 通知trigger监听者
notifySchedulerListenersSchduled(trigger); return ft;
}
- QuartzScheduler.start()源码
public void start() throws SchedulerException {
......
// 这句最关键,作用是使调度器线程跳出一个无限循环,开始轮询所有trigger触发job
// 原理详见“如何采用多线程进行任务调度”
schedThread.togglePause(false);
......
}
java--Quartz 定时执行的更多相关文章
- 关于sping quartz定时执行理解与思考
转载请注明原创出处,谢谢! 一直以为自己理解spring quartz,忽然最近几天发现自己理解的不对,在4月18号的时候,我执行了一个spring quartz的计划如下: 1 0 0 */3 * ...
- java中定时执行任务
现在项目中用到需要定时去检查文件是否更新的功能.timer正好用于此处. 用法很简单,new一个timer,然后写一个timertask的子类即可. 代码如下: package comz.autoup ...
- 用Quartz处理定时执行的任务
这次做的项目中,有一部分功能需要实现定时执行.呃,这样说可能有点笼统,打个比方吧.例如用户在登录的时候,连续输错3次密码后,系统会将该用户冻结,不再允许该用户登录系统,等到了晚上零晨时分,再为所有被冻 ...
- java 多线程——quartz 定时调度的例子
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- java Quartz定时器任务与Spring task定时的几种实现,
java Quartz定时器任务与Spring task定时的几种实现 基于java 的定时任务实现, Quartz 时间详细配置 请查阅 http://www.cnblogs.com/si ...
- Quartz.Net实现的定时执行任务调度
在之前的文章<推荐一个简单.轻量.功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler>和<简单.轻量.功能非常强大的C#/ASP.NET定时调度 ...
- Quartz定时调度jar包的执行Demo分享
1.Quartz简介 Quartz框架的核心是调度器.调度器负责管理Quartz应用运行时环境.调度器不是靠自己做所有的工作,而是依赖框架内一些非常重要的部件.Quartz不仅仅是线程和线程管理. ...
- Java 在某一个时间点定时执行任务(转载)
java定时任务,每天定时执行任务.以下是这个例子的全部代码. public class TimerManager { //时间间隔 private static final long PERIOD_ ...
- JAVA定时执行任务,每天定时几点钟执行任务
JAVA定时执行任务,每天定时几点钟执行任务的示例如下: 1.建立TimerManage类,设置时间点,时间点设置的管理类,代码如下: package com.pcitc.time; import j ...
- java web每天定时执行任务
第一步: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ...
随机推荐
- jQuery至上宝典
一 jQuery是什么? <1> jQuery由美国人John Resig创建,至今已吸引了来自世界各地的众多 javascript高手加入其team. <2>jQuery是继 ...
- Python开发【Django】:路由规则
Django路由规则 1.基于正则的URL 在templates目录下创建index.html.detail.html文件 <!DOCTYPE html> <html lang=&q ...
- django-session和cookie
在Django里面,使用Cookie和Session看起来好像是一样的,使用的方式都是request.COOKIES[XXX]和request.session[XXX],其中XXX是您想要取得的东西的 ...
- 使用Rxjava自己创建RxBus
https://piercezaifman.com/how-to-make-an-event-bus-with-rxjava-and-rxandroid/ https://lingyunzhu.git ...
- 后台管理系统-使用AdminLTE搭建前端
返回总目录<ABP项目实战-后台管理系统-目录> 安装AdminLte 我们通过Nuget包管理器安装AdminLte 引用三方组件 因为AdminLte使用到了很多三方的组件,所以我们需 ...
- 菜单条 Menu Bar Action
//.h /***Action**/ QAction * act_openImage; QAction * act_openVideo; QAction * act_openAudio; /***Me ...
- macbook 蓝牙关闭按钮灰色的
PRAM重置:1.关机,拔掉所有外设,只连接电源线2.按开机键,接着马上同时按住 command+option+p+r 键,按住后听到三次dang的开机音后放开3.电脑会自行启动,进到登录页面后点关机 ...
- KEYENCE Programming Contest 2019 Solution
A - Beginning 签到. #include <bits/stdc++.h> using namespace std; int main() { ]; while (scanf(& ...
- 【转载】package-info
本文是转载,原文地址:http://strong-life-126-com.iteye.com/blog/806246 package-info.java对于经常使用外部包的程序员来说应该是熟悉陌生人 ...
- INNODB锁(2)
在上一篇文章写了锁的基本概述以及行锁的三种形式,这一篇的主要内容如下: 一致性非锁定读 自增长与锁 外键和锁 一致性性非锁定读 一致性非锁定读是InnoDB通过多版本并发控制(MVCC,multi v ...