一、 Quartz 的原理:
Quartz 设计的核心类包括Scheduler(调度器), Job (任务)以及 Trigger(触发器)。其中,Job 负责定义需要执行的任务,Trigger 负责设置调度策略,Scheduler 将二者组装在一起,并触发任务开始执行。
二、 Quartz 的优点:
1、job 与 Trigger 的松耦合:同一个 Job 可以绑定多个不同的 Trigger,同一个Trigger不可以绑定多个job;可以修改或者替换Trigger,而不用重新定义与之关联的Job,灵活性很强。
2、提供了 listener 的功能:主要包含三种 listener:JobListener,TriggerListener 以及 SchedulerListener。当系统发生故障,相关人员需要被通知时,Listener 便能发挥它的作用。最常见的情况是,当任务被执行时,系统发生故障,Listener 监听到错误,立即发送邮件给管理员
3、持久化:即将任务调度的相关数据保存下来。默认情况 下,Quartz 采用的是 org.quartz.simpl.RAMJobStore,在这种情况下,数据仅能保存在内存中,系统重启后会全部丢失。若想持久化数据,需要采用 org.quartz.simpl.JDBCJobStoreTX。
三、 Quartz 下载、安装、配置:
1、下载quartz 2.2.3.tar.gz:http://d2zwv9pap9ylyd.cloudfront.net/quartz-2.2.3-distribution.tar.gz
2、jar包引入
a、maven项目引:
<!-- quartz 任务调度 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>
b、普通项目:
如果你希望在很多应用中使用quartz,将quartz的jar包放在应用服务器的classpath下即可。如果你只是希望在独立的应用中使用quartz,将quartz的jar包和你的应用依赖的其它jar包放在一起即可。
四、 Quartz 相关类介绍:
1、Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
2、Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。SimpleTrigger:当仅需触发一次或者以固定时间间隔周期执行;CronTrigger:可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;
3、Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。
4、JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。
5、Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。
6、ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
五、 Quartz 的使用:
import static org.quartz.DateBuilder.futureDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.JobKey.jobKey;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import java.util.Date;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzController implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("yyx's jobTest:Description=" + arg0.getJobDetail().getKey() + ",type=" + arg0.getJobDetail().getJobDataMap().get("type"));
System.out.println(new Date());
}
public static void main(String[] args) {
try {
// 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();//调度开启
// 创建该Job负责定义需要执行任务
JobDetail job1 = newJob(QuartzController.class).withIdentity("job1", "group1").usingJobData("type", "yyx1").build();
// 创建触发器立即执行 默认执行一次
SimpleTrigger trigger1 = (SimpleTrigger) newTrigger().withIdentity("myTrigger1", "group1").startNow().build();
Date df = scheduler.scheduleJob(job1, trigger1);// 执行一次
// 立即执行 5s一次执行5次
JobDetail job2 = newJob(QuartzController.class).withIdentity("job2", "group1").usingJobData("type", "yyx2").build();
SimpleTrigger trigger2 = newTrigger().withIdentity("myTrigger2", "group2").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(5).withRepeatCount(5)).build();
df = scheduler.scheduleJob(job2, trigger2);
// 给触发器绑定任务forJob(job3)
JobDetail job3 = newJob(QuartzController.class).withIdentity("job3", "group1").usingJobData("type", "yyx3").build();
SimpleTrigger trigger3 = newTrigger().withIdentity("myTrigger3", "group3").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(5).withRepeatCount(5)).forJob(job3).build();
df = scheduler.scheduleJob(job3, trigger3);
// 设定5分钟后运行futureDate(5, IntervalUnit.MINUTE)
JobDetail job4 = newJob(QuartzController.class).withIdentity("job4", "group1").usingJobData("type", "yyx4").build();
SimpleTrigger trigger4 = (SimpleTrigger) newTrigger().withIdentity("trigger4", "group4").startAt(futureDate(5, IntervalUnit.MINUTE)).build();
df = scheduler.scheduleJob(job4, trigger4);
// 一直执行repeatForever()
JobDetail job5 = newJob(QuartzController.class).withIdentity("job5", "group1").usingJobData("type", "yyx5").build();
SimpleTrigger trigger5 = newTrigger().withIdentity("myTrigger5", "group5").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
df = scheduler.scheduleJob(job5, trigger5);
// 手动触发 storeDurably:是否有触发器
JobDetail job6 = newJob(QuartzController.class).withIdentity("job6", "group1").storeDurably().build();
scheduler.addJob(job6, true);
scheduler.triggerJob(jobKey("job6", "group1"));
try {
Thread.sleep(5 * 60 * 1000);// 等待5分钟
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduler.shutdown();// 关闭调度
SchedulerMetaData meta = scheduler.getMetaData();
System.out.println("已经执行的job个数:" + meta.getNumberOfJobsExecuted());
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
注意:同一个Trigger不可以绑定多个job,如下代码运行时会报下面的错误。
try {
scheduler.scheduleJob(job1, trigger1);
Thread.sleep(2 * 1000);
scheduler.scheduleJob(job2, trigger1);
Thread.sleep(2 * 1000);
scheduler.scheduleJob(job3, trigger1);
} catch (InterruptedException e) {
e.printStackTrace();
}
参考文献:
1、http://www.quartz-scheduler.org/documentation/quartz-2.2.x/quick-start.html
2、http://ifeve.com/quartz-tutorial-job-jobdetail/
3、http://7-sun.com/doc/quartz1.8/org/quartz/package-summary.html