定时组件quartz系列<一>模拟定时组件小程序

时间:2023-03-08 16:15:34
定时组件quartz系列<一>模拟定时组件小程序

一、核心概念
 
Quartz的原理不是很复杂,只要搞明白几个概念,然后知道如何去启动和关闭一个调度程序即可。
 
1、Job
表示一个工作,要执行的具体内容。此接口中只有一个方法
void execute(JobExecutionContext context)
 
2、JobDetail
JobDetail表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。
 
3、Trigger代表一个调度参数的配置,什么时候去调。
 
4、Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。

二模拟案例

【1】定时任务类

 package com.yeepay.sxf.testQuartz;

 import java.util.Map;
/**
* job类,这个类非常简单,只有一个execute方法,该方法是定时job具体执行的内容
* 也就是定时任务
* @author sxf
*
*/
public class Job {
public void exectute(Map<String, String> jobData){
System.out.println("******************");
System.out.println(jobData.get("type")+":Test Job Run at:"+System.currentTimeMillis());
System.out.println("******************");
}
}

【2】定时任务类的详细信息

 package com.yeepay.sxf.testQuartz;

 import java.util.HashMap;
/**
* 定时的详细信息
* 定时的相信信息(1)这个定时信息对应的任务类
* (2)这个定时信息对应的任务类的参数,方法
* @author sxf
*
*/
public class JobDetail { private Class<? extends Job> clazz;
private String jobName;
private HashMap<String, String> jobData; //无参构造
public JobDetail(){
jobData =new HashMap<String, String>();
}
//有参构造
public JobDetail(String name,Class<? extends Job> clazz){
this();
this.jobName=name;
this.clazz=clazz;
} public int hashCode(){
final int prime=31;
int result=1;
result=prime*result+((jobName==null)?0:jobName.hashCode());
return result;
} /**
* 用来通过在list中使用key匹配获取jobdetail
*/
@Override
public boolean equals(Object obj) {
if(this==obj){
return true;
}
if(obj==null){
return false;
}
JobDetail other=(JobDetail) obj;
if(jobName==null){
if(other.jobName!=null){
return false;
}
}else if(!jobName.equals(other.jobName)){
return false;
}
return true;
}
public Class<? extends Job> getClazz() {
return clazz;
}
public void setClazz(Class<? extends Job> clazz) {
this.clazz = clazz;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public HashMap<String, String> getJobData() {
return jobData;
}
public void setJobData(HashMap<String, String> jobData) {
this.jobData = jobData;
} }

【3】定时的触发器

 package com.yeepay.sxf.testQuartz;
/**
* Trigger类,记录下次运行作业的时间和运行job的key
* 一个触发器(1)内部维护一个定时任务的执行策略
* (2)内部同时维护一个定时的详细信息
* (3)触发器和定时详细信息是一对一的关系
* @author sxf
*
*/
public class Trigger implements Comparable { /**
* 定时的详细信息
*/
private String jobKey;
/**
* 定时的执行策略(下次执行时间)
*/
private long nextFireTime; /**
* 在TreeMap中可以根据下次运行时间排序
*/
@Override
public int compareTo(Object o) {
Trigger d=(Trigger) o;
return (int)(this.nextFireTime-d.getNextFireTime());
} /**
* 测试是只想运行一次,使用-1来退出
*/
public void resert(){
setNextFireTime(-1);
} public String getJobKey() {
return jobKey;
} public void setJobKey(String jobKey) {
this.jobKey = jobKey;
} public long getNextFireTime() {
return nextFireTime;
} public void setNextFireTime(long nextFireTime) {
this.nextFireTime = nextFireTime;
} }

【4】定时的初始化类(最关键的类)

 package com.yeepay.sxf.testQuartz;

 import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TreeSet; /**
* Sucheduler类,最重要的类,用来启动和停止框架
* (1)内部维护触发器的集合
* (2)内部维护定时详细信息
* (3)内部维护一个线程类。该线程类是调度定时执行的关键
* @author sxf
*
*/
public class Scheduler { private List<JobDetail> jobList=new ArrayList<JobDetail>();
private TreeSet<Trigger> triggerList=new TreeSet<Trigger>();
private Object lockObject=new Object();
SchedulerThread thread; public void schedulerJob(JobDetail detail,Trigger trigger){
synchronized (lockObject) {
jobList.add(detail);
trigger.setJobKey(detail.getJobName());
triggerList.add(trigger); }
} public void start(){
this.thread=new SchedulerThread(lockObject, triggerList, jobList);
System.out.println("########## run scheduler at:"+new Date()+"##########");
thread.start();
} public void halt(){
thread.halt();
}
}

【5】定时任务调度的线程类

 package com.yeepay.sxf.testQuartz;

 import java.util.List;
import java.util.TreeSet; /**
* 任务调度线程
* (1)从触发器集合中获取触发器
* (2)根据获取的触发器,确认执行策略,和定时的详细信息
* (3)如果符合当前执行策略,则触发器和定时详细信息配合执行定时任务
* @author sxf
*
*/
public class SchedulerThread extends Thread{ private Object lockObject;
private boolean shutDown=false;
private TreeSet<Trigger> triggerList;
private List<JobDetail> jobList; public SchedulerThread(Object lockObject,TreeSet<Trigger> triggerList,List<JobDetail> joblist){
this.lockObject=lockObject;
this.triggerList=triggerList;
this.jobList=joblist;
} @Override
public void run() { while(!shutDown){
synchronized (lockObject) {
try {
//获取最近的触发器
final Trigger trigger=triggerList.pollFirst();//获取最近执行的作业
if(trigger==null){
lockObject.wait(100);
continue;
}
long curr=System.currentTimeMillis();
//从触发器中获取该触发器对应的执行策略
long nextTime=trigger.getNextFireTime();
while(nextTime>curr&&!shutDown){
curr=System.currentTimeMillis();
if(nextTime>curr+1){
lockObject.wait(nextTime-curr);
}
if(!shutDown){
//获取最早的定时任务在集合中的索引
int index=jobList.indexOf(new JobDetail(trigger.getJobKey(), null));
//获取最早的定时
JobDetail jobDetail=jobList.get(index);
//利用反射机制,获取定时任务的类
Job job=jobDetail.getClazz().newInstance();
//执行定时任务
job.exectute(jobDetail.getJobData());
trigger.resert();
nextTime=trigger.getNextFireTime();
if(nextTime!=-1){
triggerList.add(trigger);
}else{
break;
} }
}
} catch (Exception e) {
e.printStackTrace();
}finally{ }
}
}
} public void halt(){
synchronized (lockObject) {
shutDown=true;
lockObject.notifyAll();
}
} }

【6】客户端测试类

 package com.yeepay.sxf.testQuartz;

 /**
* 客户端测试
* 该定时模拟,和jdk自带的TimerTask很相似。
* @author sxf
*
*/
public class ClientTest { public static void main(String[] args) {
final JobDetail detail1=new JobDetail("job1",Job.class);
detail1.getJobData().put("type", "job1");
final JobDetail detail2=new JobDetail("job2",Job.class);
detail2.getJobData().put("type", "job2");
final Trigger trigger1=new Trigger();
trigger1.setNextFireTime(System.currentTimeMillis()+30001);
final Trigger trigger2=new Trigger();
trigger2.setNextFireTime(System.currentTimeMillis()+10001); Scheduler scheduler=new Scheduler();
scheduler.schedulerJob(detail1, trigger1);
scheduler.schedulerJob(detail2, trigger2); scheduler.start();
try {
Thread.sleep(100001);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
scheduler.halt();
}
}