quartz源碼解析(一)

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

任何個人、任何企業、任何行業都會有作業調度的需求。舉幾個簡單的例子:

1、每個月都會發工資,每個月都要報銷等;

2、每個人每天都要喫飯和睡覺;

3、每個公司都有會計帳期,都需要向公司領導彙報月報、季報、年報等。

4、銀行和金融行業每天都需要日切等等。

 

對於個人,對作業調度的感知不是非常明確,但對於一個企業,一個好的作業調度可以爲企業節省很多時間和金錢。俗話說,時間就是金錢,過高的資源投入到枯燥的任務中無疑是金錢和資源的浪費,隨着業務流程複雜性的提高,自動化流程和自動化作業調度更能凸顯出益處來。之所以自動化的出現,因爲在做重複性工作時,人的效率和正確率遠低於電腦。把一系列任務自動安排到一個作業中,然後再爲這個作業創建一個調度器,到時候這個作業就會自動執行了。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章