Quartz是一個特性豐富的、開源的做業調度框架。它能夠集成到任何Java應用。
使用它,你能夠很是輕鬆的實現定時任務的調度執行。
場景1:提醒和告警
場景2:監聽事務
場景3:定時做業
1.能夠直接在官網:http://www.quartz-scheduler.org/ 下載jar包。
2.若是使用maven,能夠在pom.xml中添加如下依賴jar包:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
|
Github地址:https://github.com/quartz-scheduler/quartz
開始學習以前,慣例仍是show一下Hello World。
例:
1.先定義一個Job
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(String.format("Hello World! Time:%s", new Date()));
}
}
|
2.定義Job和Trigger去調度咱們定義的HelloJob。
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.zp.tent.scheduler.demo.job.HelloJob;
/**
* @Title HelloQuartz
* @Description Quartz的Hello World實例
* @Author zhangpeng
* @Date 2016年7月6日
*/
public class HelloWorldDemo {
public static void main(String[] args) {
try {
// 經過schedulerFactory獲取一個調度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 經過schedulerFactory獲取一個調度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 建立jobDetail實例,綁定Job實現類
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build();
// 定義調度觸發規則,本例中使用SimpleScheduleBuilder建立了一個5s執行一次的觸發器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.build();
// 把做業和觸發器註冊到任務調度中
scheduler.scheduleJob(jobDetail, trigger);
// 啓動調度
scheduler.start();
// 60s後關閉
Thread.sleep(1000 * 30);
scheduler.shutdown();
System.out.println("調度任務結束");
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
好了,運行一下試試吧。
做用:Scheduler接口是Quartz最核心的接口。Scheduler維護着JobDetail和Trigger的註冊信息。一旦註冊成功,Scheduler負責執行和Job關聯的觸發器。
一個Scheduler實例能夠視爲一個調度做業容器。能夠經過start和shutdown方法來控制它的生命週期。
例:
// 經過schedulerFactory獲取一個調度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 經過schedulerFactory獲取一個調度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 啓動
scheduler.start();
…
//關閉
scheduler.shutdown();
|
做用:開發者實現該接口定義須要執行的做業。JobExecutionContext類提供調度上下文的各類信息。
實現Job接口的類還可使用註解進行修飾。
@DisallowConcurrentExecution:此註解表示不容許這個Job併發執行
@PersistJobDataAfterExecution:此註解表示當這個Job的execute方法執行成功後,更新並存儲它所持有的JobDetail屬性中JobDataMap。若是使用這個註解,強烈建議也使用@DisallowConcurrentExecution,由於併發執行過程當中,JobDataMap有可能會發生衝突。
例:
public class xxxJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
…
}
}
|
做用:用於定義Job實例。
JobDetail有兩個boolean屬性。
isDurable:若是設爲false,則對應的Job一旦沒有關聯的觸發器,就會被Scheduler自動刪除。
requestsRecovery:若是設爲true,當Job執行中遇到硬中斷(例如運行崩潰、機器斷電等),Scheduler會從新執行。這種狀況下,JobExecutionContext.isRecovering()會返回ture。
做用:用於定義、構建JobDetail實例。
例:
// 建立jobDetail實例,綁定Job實現類
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build();
|
做用:定義Job執行的觸發規則。
Quartz中有多種觸發器,最經常使用的是SimpleTrigger 和 CronTrigger。
SimpleTrigger通常用於只執行一次或在指定時間執行的做業;CronTrigger通常用於週期性執行(例如,每日執行、每週執行)的做業,須要按照指定的時間表達式規則設置調度時間。
Priority:這個屬性表示Trigger的權重。當兩個Trigger觸發時間相同時,權重大的那個先執行。Quartz默認的權重值爲5。
Misfire Instruction:在Trigger接口中能夠設置錯過觸發處理機制。就是說在指定觸發的時間點因爲某種緣由錯過執行的時機了,這時如何去處理。Quartz提供了多種策略,這裏不詳述,有興趣的能夠參考官方文檔。
Job和Trigger的關係
多個Job能夠依賴於一個Trigger;多個Trigger也能夠關聯一個Job。
可是,從最佳實踐來看,最好讓Job和Trigger保持一對多的關係,這樣更便於管理。
做用:用於定義、構建Trigger實例。
例:
下面兩種方式是同樣的效果,都是建立一個每5s執行一次的觸發器
// 定義調度觸發規則, SimpleScheduleBuilder方式
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
// 定義調度觸發規則, CronScheduleBuilder方式
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();
|
第二種觸發器構建方式中使用了形如"0/5 * * * * ?"的CronExpression表達式來建立觸發器規則。這裏不在細說,在下文的CronExpression表達式一節再詳述。
JobDetail接口中持有JobDataMap類。開發者能夠將做業執行時須要的參數或對象填入這個類中。
填入數據和獲取數據的方式很相似Json。
例:
先定義一個Job
public class WithJobDataMapJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
// 基本信息
JobKey jobKey = context.getJobDetail().getKey();
TriggerKey triggerKey = context.getTrigger().getKey();
// 獲取JobDataMap的方式:若是是基本類型,JobDataMap提供了多種get方法;若是是引用類型,能夠直接get,而後進行強制轉換
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
Student student = (Student) dataMap.get("student");
List<String> interests = (List<String>) dataMap.get("interests");
String word = dataMap.getString("word");
System.out.println(String.format("[JobKey:%s][TriggerKey:%s] of DumbJob print info:", jobKey, triggerKey));
System.out.println(String.format("[Student]name:%s, age:%d, sex:%s", student.getName(), student.getAge(),
student.getSex()));
StringBuilder interestsStr = new StringBuilder();
for (String item : interests) {
interestsStr.append(item + " ");
}
System.out.println("His interests ars: " + interestsStr.toString());
System.out.println("He want to say: " + word);
System.out.println("===================================");
}
}
|
客戶端代碼:
public static void main(String[] args) {
try {
// 經過schedulerFactory獲取一個調度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 經過schedulerFactory獲取一個調度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 建立jobDetail實例,綁定Job實現類
JobDetail jobDetail = JobBuilder.newJob(WithJobDataMapJob.class).withIdentity("myJob", "group1").build();
// 使用JobDataMap填入想要攜帶的特殊信息。能夠填入基本數據類型、字符串、集合,甚至是一個對象。填入方式很相似JSON
Student student = new Student("Jack", 20, "male");
List<String> interests = new ArrayList<String>();
interests.add("dancing");
interests.add("singing");
interests.add("swimming");
String word = "Hello World!";
JobDataMap map = jobDetail.getJobDataMap();
map.put("student", student);
map.put("interests", interests);
map.put("word", word);
// 定義調度觸發規則,本例中使用SimpleScheduleBuilder建立了一個5s執行一次的觸發器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.build();
// 把做業和觸發器註冊到任務調度中
scheduler.scheduleJob(jobDetail, trigger);
// 啓動調度
scheduler.start();
// 60s後關閉
Thread.sleep(1000 * 30);
scheduler.shutdown();
System.out.println("調度任務結束");
} catch (Exception e) {
e.printStackTrace();
}
}
|
在Quartz中,能夠分別經過JobKey和TriggerKey來惟一地識別一個Job或一個Trigger。
這兩個Key都有兩個關鍵屬性:name和group。
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();
|
還記得上文中展現的使用CronScheduleBuilder方式構建觸發器時的例子嗎?在這個例子中,咱們使用的表達式字符串"0/5 * * * * ?"是什麼意思呢?閱讀本節後,你就會了解了。
一個cron表達式有至少6個(也可能7個)有空格分隔的時間元素。
CronTrigger配置完整格式爲: [秒] [分] [小時] [日] [月] [周] [年]
參數設置規則見下表
字段
|
容許值
|
容許的特殊字符
|
秒
|
0-59
|
, - * /
|
分
|
0-59
|
, - * /
|
小時
|
0-23
|
, - * /
|
日期
|
1-31
|
, - * ? / L W
|
月份
|
1-12 或者 JAN-DEC
|
, - * /
|
星期
|
1-7 或者 SUN-SAT
|
, - * ? / L #
|
年(可選)
|
留空, 1970-2099
|
, - * /
|
表 cronExpression表達式參數
表示全部值。
例如:在分的字段上設置 "*",表示每一分鐘都會觸發。
表示不指定值。使用的場景爲不須要關心當前設置這個字段的值。
例如:要在每個月的10號觸發一個操做,但不關心是周幾,因此須要周位置的那個字段設置爲"?" 具體設置爲 0 0 0 10 * ?
表示區間。
例如在小時上設置 "10-12",表示 10,11,12點都會觸發。
表示指定多個值。
例如在周字段上設置 "MON,WED,FRI" 表示週一,週三和週五觸發
用於遞增觸發。如在秒上面設置"5/15" 表示從5秒開始,每增15秒觸發(5,20,35,50)。在月字段上設置'1/3'所示每個月1號開始,每隔三天觸發一次。
表示最後的意思。
例如在日字段設置上,表示當月的最後一天(依據當前月份,若是是二月還會依據是不是潤年[leap]), 在周字段上表示星期六,至關於"7"或"SAT"。若是在"L"前加上數字,則表示該數據的最後一個。例如在周字段上設置"6L"這樣的格式,則表示「本月最後一個星期五"
表示離指定日期的最近那個工做日(週一至週五)。
例如在日字段上設置"15W",表示離每個月15號最近的那個工做日觸發。若是15號正好是週六,則找最近的週五(14號)觸發, 若是15號是周未,則找最近的下週一(16號)觸發。若是15號正好在工做日(週一至週五),則就在該天觸發。若是指定格式爲 "1W",它則表示每個月1號日後最近的工做日觸發。若是1號正是週六,則將在3號下週一觸發。(注,"W"前只能設置具體的數字,不容許區間"-")。
小提示:'L'和 'W'能夠一組合使用。若是在日字段上設置"LW",則表示在本月的最後一個工做日觸發;周字段的設置,若使用英文字母是不區分大小寫的,即MON與mon相同。
表示每個月的第幾個周幾。
例如在周字段上設置"6#3"表示在每個月的第三個週六。注意若是指定"#5",正好第五週沒有周六,則不會觸發該配置(用在母親節和父親節再合適不過了)。
注:表中月份一行的JAN-DEC,是指一月到十二月的英文縮寫;星期一行的SUN-SAT,是指星期天到星期六的英文縮寫。
案例
|
意義
|
"0 0 12 * * ?"
|
天天中午12點觸發
|
"0 15 10 ? * *"
|
天天上午10:15觸發
|
"0 15 10 * * ?"
|
天天上午10:15觸發
|
"0 15 10 * * ? *"
|
天天上午10:15觸發
|
"0 15 10 * * ? 2005"
|
2005年的天天上午10:15 觸發
|
"0 * 14 * * ?"
|
在天天下午2點到下午2:59期間的每1分鐘觸發
|
"0 0/5 14 * * ?"
|
在天天下午2點到下午2:55期間的每5分鐘觸發
|
"0 0/5 14,18 * * ?"
|
在天天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
|
"0 0-5 14 * * ?"
|
在天天下午2點到下午2:05期間的每1分鐘觸發
|
"0 10,44 14 ? 3 WED"
|
每一年三月的星期三的下午2:10和2:44觸發
|
"0 15 10 ? * MON-FRI"
|
週一至週五的上午10:15觸發
|
"0 15 10 15 * ?"
|
每個月15日上午10:15觸發
|
"0 15 10 L * ?"
|
每個月最後一日的上午10:15觸發
|
"0 15 10 ? * 6L"
|
每個月的最後一個星期五上午10:15觸發
|
"0 15 10 ? * 6L 2002-2005"
|
2002年至2005年的每個月的最後一個星期五上午10:15觸發
|
"0 15 10 ? * 6#3"
|
每個月的第三個星期五上午10:15觸發
|
官方文檔:http://www.quartz-scheduler.org/documentation/
官方2.2版本教程:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/