天天看点

quartz源码解析(一)

原文地址:http://smurfs.iteye.com/blog/1155965

任何个人、任何企业、任何行业都会有作业调度的需求。举几个简单的例子:

1、每个月都会发工资,每个月都要报销等;

2、每个人每天都要吃饭和睡觉;

3、每个公司都有会计帐期,都需要向公司领导汇报月报、季报、年报等。

4、银行和金融行业每天都需要日切等等。

对于个人,对作业调度的感知不是非常明确,但对于一个企业,一个好的作业调度可以为企业节省很多时间和金钱。俗话说,时间就是金钱,过高的资源投入到枯燥的任务中无疑是金钱和资源的浪费,随着业务流程复杂性的提高,自动化流程和自动化作业调度更能凸显出益处来。之所以自动化的出现,因为在做重复性工作时,人的效率和正确率远低于电脑。把一系列任务自动安排到一个作业中,然后再为这个作业创建一个调度器,到时候这个作业就会自动执行了。

Quartz是经典的作业调度框架,在深入研究之初,先仿照quartz的原理,设计一个简单的作业调度器,类图如下:

quartz源码解析(一)

1、  先看job类,这个类,非常简单,只有一个execute方法,该方法是job具体执行的内容:

public void execute(Map<String, String> jobData) {
		System.out.println("####################");
		System.out.println(jobData.get("type") + ":Test Job Run at :" + System.currentTimeMillis());
		System.out.println("####################");
	}
           

2、  jobdetail类,该类是对具体job类的封装,包括jobName(id),job执行需要的运行时参数,在名为jobdata的hashMap中

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;
	}
           
@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((jobName == null) ? 0 : jobName.hashCode());
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@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;
	}
           

3、  trigger类,记录下次运行作业的时间和运行job的key:

private String jobKey;
	private long nextFireTime;

	@Override
	public int compareTo(Trigger o) {// 在treemap中可以根据下次运行时间排序
		return (int) (this.nextFireTime - o.getNextFireTime());
	}

	public void resert() {
		setNextFireTime(-1);// 测试是只想运行一次,使用-1来退出
	}
           

4、  scheduler类,最重要的类,用来启动和停止框架

private List<JobDetail> jobList = new ArrayList<JobDetail>();
private TreeSet<Trigger> triggerList = new TreeSet<Trigger>();
private Object lockObj = new Object();
SchedulerThread thread = new SchedulerThread();// 任务调度在本此线程执行

public void schedulerJob(JobDetail detail, Trigger trigger) {
	synchronized (lockObj) {
		jobList.add(detail);
		trigger.setJobKey(detail.getJobName());
		triggerList.add(trigger);
	}
}

public void start() {
	System.out.println("########## run scheduler at :" + new Date() + "##########");
	thread.start();
}

public void halt() {
	thread.halt();
}
           

5、  scheduler的执行是在scheduler的schedulerThread中执行;线程中最重要的是run方法体,另外还有一个halt方法用来停止线程。先看halt方法

private boolean shutDown = false;

public void halt() {// 停止线程
	synchronized (lockObj) {
		shutDown = true;
		lockObj.notifyAll();
	}
}
           

Run方法体为:

public void run() {// 运行
<span style="white-space:pre">	</span>while (!shutDown)
		synchronized (lockObj) {
	<span style="white-space:pre">	</span>	try {
				final Trigger trigger = triggerList.pollFirst();// 获取最近执行的作业
		<span style="white-space:pre">	</span>		if (trigger == null) {
						lockObj.wait(100);
						continue;
					}
					long curr = System.currentTimeMillis();
					long nextTime = trigger.getNextFireTime();
					while (nextTime > curr && !shutDown) {
						curr = System.currentTimeMillis();
						if (nextTime > curr + 1) {
							lockObj.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.execute(jobDetail.getJobData());
							trigger.resert();
							nextTime = trigger.getNextFireTime();
							if (nextTime != -1) {
								triggerList.add(trigger);
							} else {
								break;
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
	}
           

至此所有的框架代码都已经完成。注:没有考虑NullPointException和多线程问题。

再看下测试代码:

public static void main(String[] args) throws Exception {
		final JobDetail detail1 = new JobDetail("job1", Job.class) {
			{
				getJobData().put("type", "job1");
			}
		};
		final JobDetail detail2 = new JobDetail("job2", Job.class) {
			{
				getJobData().put("type", "job2");
			}
		};
		final Trigger trigger1 = new Trigger() {
			{
				setNextFireTime(System.currentTimeMillis() + 3000l);
			}
		};
		final Trigger trigger2 = new Trigger() {
			{
				setNextFireTime(System.currentTimeMillis() + 1000l);
			}
		};

		Scheduler scheduler = new Scheduler();
		scheduler.schedulerJob(detail1, trigger1);
		scheduler.schedulerJob(detail2, trigger2);

		scheduler.start();
		Thread.sleep(10000l);
		scheduler.halt();
	}
           

结果

########## run scheduler at :Fri Aug 14 17:09:42 CST 2015##########
####################
job2:Test Job Run at :1439543383133
####################
####################
job1:Test Job Run at :1439543385133
####################
           

系统按照下次运行时间来执行,并成功显示结果。

本章只是在抽取quartz的核心处理逻辑的基础智商,去除安全验证和多线程同步问题编写的基本调度任务。仅为抛砖引玉,为quartz源码的解析打个基础。

源码地址:https://github.com/luyanliang/quartz_demo.git

继续阅读