原文地址: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源碼的解析打個基礎。