一、核心概念
Quartz的原理不是很复杂,只要搞明白几个概念,然后知道如何去启动和关闭一个调度程序即可。
1、Job
表示一个工作,要执行的具体内容。此接口中只有一个方法
void execute(JobExecutionContext context)
2、JobDetail
JobDetail表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。
3、Trigger代表一个调度参数的配置,什么时候去调。
4、Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。
二模拟案例
【1】定时任务类
1 package com.yeepay.sxf.testQuartz; 2 3 import java.util.Map; 4 /** 5 * job类,这个类非常简单,只有一个execute方法,该方法是定时job具体执行的内容 6 * 也就是定时任务 7 * @author sxf 8 * 9 */ 10 public class Job { 11 public void exectute(Map<String, String> jobData){ 12 System.out.println("******************"); 13 System.out.println(jobData.get("type")+":Test Job Run at:"+System.currentTimeMillis()); 14 System.out.println("******************"); 15 } 16 }
【2】定时任务类的详细信息
1 package com.yeepay.sxf.testQuartz; 2 3 import java.util.HashMap; 4 /** 5 * 定时的详细信息 6 * 定时的相信信息(1)这个定时信息对应的任务类 7 * (2)这个定时信息对应的任务类的参数,方法 8 * @author sxf 9 * 10 */ 11 public class JobDetail { 12 13 private Class<? extends Job> clazz; 14 private String jobName; 15 private HashMap<String, String> jobData; 16 17 //无参构造 18 public JobDetail(){ 19 jobData =new HashMap<String, String>(); 20 } 21 //有参构造 22 public JobDetail(String name,Class<? extends Job> clazz){ 23 this(); 24 this.jobName=name; 25 this.clazz=clazz; 26 } 27 28 public int hashCode(){ 29 final int prime=31; 30 int result=1; 31 result=prime*result+((jobName==null)?0:jobName.hashCode()); 32 return result; 33 } 34 35 /** 36 * 用来通过在list中使用key匹配获取jobdetail 37 */ 38 @Override 39 public boolean equals(Object obj) { 40 if(this==obj){ 41 return true; 42 } 43 if(obj==null){ 44 return false; 45 } 46 JobDetail other=(JobDetail) obj; 47 if(jobName==null){ 48 if(other.jobName!=null){ 49 return false; 50 } 51 }else if(!jobName.equals(other.jobName)){ 52 return false; 53 } 54 return true; 55 } 56 public Class<? extends Job> getClazz() { 57 return clazz; 58 } 59 public void setClazz(Class<? extends Job> clazz) { 60 this.clazz = clazz; 61 } 62 public String getJobName() { 63 return jobName; 64 } 65 public void setJobName(String jobName) { 66 this.jobName = jobName; 67 } 68 public HashMap<String, String> getJobData() { 69 return jobData; 70 } 71 public void setJobData(HashMap<String, String> jobData) { 72 this.jobData = jobData; 73 } 74 75 76 }
【3】定时的触发器
1 package com.yeepay.sxf.testQuartz; 2 /** 3 * Trigger类,记录下次运行作业的时间和运行job的key 4 * 一个触发器(1)内部维护一个定时任务的执行策略 5 * (2)内部同时维护一个定时的详细信息 6 * (3)触发器和定时详细信息是一对一的关系 7 * @author sxf 8 * 9 */ 10 public class Trigger implements Comparable { 11 12 /** 13 * 定时的详细信息 14 */ 15 private String jobKey; 16 /** 17 * 定时的执行策略(下次执行时间) 18 */ 19 private long nextFireTime; 20 21 /** 22 * 在TreeMap中可以根据下次运行时间排序 23 */ 24 @Override 25 public int compareTo(Object o) { 26 Trigger d=(Trigger) o; 27 return (int)(this.nextFireTime-d.getNextFireTime()); 28 } 29 30 /** 31 * 测试是只想运行一次,使用-1来退出 32 */ 33 public void resert(){ 34 setNextFireTime(-1); 35 } 36 37 public String getJobKey() { 38 return jobKey; 39 } 40 41 public void setJobKey(String jobKey) { 42 this.jobKey = jobKey; 43 } 44 45 public long getNextFireTime() { 46 return nextFireTime; 47 } 48 49 public void setNextFireTime(long nextFireTime) { 50 this.nextFireTime = nextFireTime; 51 } 52 53 54 55 }
【4】定时的初始化类(最关键的类)
1 package com.yeepay.sxf.testQuartz; 2 3 import java.util.ArrayList; 4 import java.util.Date; 5 import java.util.List; 6 import java.util.TreeSet; 7 8 9 10 /** 11 * Sucheduler类,最重要的类,用来启动和停止框架 12 * (1)内部维护触发器的集合 13 * (2)内部维护定时详细信息 14 * (3)内部维护一个线程类。该线程类是调度定时执行的关键 15 * @author sxf 16 * 17 */ 18 public class Scheduler { 19 20 private List<JobDetail> jobList=new ArrayList<JobDetail>(); 21 private TreeSet<Trigger> triggerList=new TreeSet<Trigger>(); 22 private Object lockObject=new Object(); 23 SchedulerThread thread; 24 25 public void schedulerJob(JobDetail detail,Trigger trigger){ 26 synchronized (lockObject) { 27 jobList.add(detail); 28 trigger.setJobKey(detail.getJobName()); 29 triggerList.add(trigger); 30 31 } 32 } 33 34 public void start(){ 35 this.thread=new SchedulerThread(lockObject, triggerList, jobList); 36 System.out.println("########## run scheduler at:"+new Date()+"##########"); 37 thread.start(); 38 } 39 40 public void halt(){ 41 thread.halt(); 42 } 43 }
【5】定时任务调度的线程类
1 package com.yeepay.sxf.testQuartz; 2 3 import java.util.List; 4 import java.util.TreeSet; 5 6 /** 7 * 任务调度线程 8 * (1)从触发器集合中获取触发器 9 * (2)根据获取的触发器,确认执行策略,和定时的详细信息 10 * (3)如果符合当前执行策略,则触发器和定时详细信息配合执行定时任务 11 * @author sxf 12 * 13 */ 14 public class SchedulerThread extends Thread{ 15 16 private Object lockObject; 17 private boolean shutDown=false; 18 private TreeSet<Trigger> triggerList; 19 private List<JobDetail> jobList; 20 21 public SchedulerThread(Object lockObject,TreeSet<Trigger> triggerList,List<JobDetail> joblist){ 22 this.lockObject=lockObject; 23 this.triggerList=triggerList; 24 this.jobList=joblist; 25 } 26 27 @Override 28 public void run() { 29 30 while(!shutDown){ 31 synchronized (lockObject) { 32 try { 33 //获取最近的触发器 34 final Trigger trigger=triggerList.pollFirst();//获取最近执行的作业 35 if(trigger==null){ 36 lockObject.wait(100); 37 continue; 38 } 39 long curr=System.currentTimeMillis(); 40 //从触发器中获取该触发器对应的执行策略 41 long nextTime=trigger.getNextFireTime(); 42 while(nextTime>curr&&!shutDown){ 43 curr=System.currentTimeMillis(); 44 if(nextTime>curr+1){ 45 lockObject.wait(nextTime-curr); 46 } 47 if(!shutDown){ 48 //获取最早的定时任务在集合中的索引 49 int index=jobList.indexOf(new JobDetail(trigger.getJobKey(), null)); 50 //获取最早的定时 51 JobDetail jobDetail=jobList.get(index); 52 //利用反射机制,获取定时任务的类 53 Job job=jobDetail.getClazz().newInstance(); 54 //执行定时任务 55 job.exectute(jobDetail.getJobData()); 56 trigger.resert(); 57 nextTime=trigger.getNextFireTime(); 58 if(nextTime!=-1){ 59 triggerList.add(trigger); 60 }else{ 61 break; 62 } 63 64 } 65 } 66 } catch (Exception e) { 67 e.printStackTrace(); 68 }finally{ 69 70 } 71 } 72 } 73 } 74 75 public void halt(){ 76 synchronized (lockObject) { 77 shutDown=true; 78 lockObject.notifyAll(); 79 } 80 } 81 82 }
【6】客户端测试类
1 package com.yeepay.sxf.testQuartz; 2 3 /** 4 * 客户端测试 5 * 该定时模拟,和jdk自带的TimerTask很相似。 6 * @author sxf 7 * 8 */ 9 public class ClientTest { 10 11 public static void main(String[] args) { 12 final JobDetail detail1=new JobDetail("job1",Job.class); 13 detail1.getJobData().put("type", "job1"); 14 final JobDetail detail2=new JobDetail("job2",Job.class); 15 detail2.getJobData().put("type", "job2"); 16 final Trigger trigger1=new Trigger(); 17 trigger1.setNextFireTime(System.currentTimeMillis()+30001); 18 final Trigger trigger2=new Trigger(); 19 trigger2.setNextFireTime(System.currentTimeMillis()+10001); 20 21 Scheduler scheduler=new Scheduler(); 22 scheduler.schedulerJob(detail1, trigger1); 23 scheduler.schedulerJob(detail2, trigger2); 24 25 scheduler.start(); 26 try { 27 Thread.sleep(100001); 28 } catch (InterruptedException e) { 29 // TODO Auto-generated catch block 30 e.printStackTrace(); 31 } 32 scheduler.halt(); 33 } 34 }