簡介
Quartz是什么?
Quartz是一個特性豐富的、開源的作業調度框架。它可以集成到任何Java應用。
使用它,你可以非常輕松的實現定時任務的調度執行。
Quartz的應用場景
場景1:提醒和告警
場景2:監聽事務
場景3:定時作業
Quartz的安裝
安裝
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
Hello World范例
開始學習之前,慣例還是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();
}
}
}
|
好了,運行一下試試吧。
API
核心API
Scheduler接口:
作用:Scheduler接口是Quartz最核心的接口。Scheduler維護着JobDetail和Trigger的注冊信息。一旦注冊成功,Scheduler負責執行和Job關聯的觸發器。
一個Scheduler實例可以視為一個調度作業容器。可以通過start和shutdown方法來控制它的生命周期。
例:
// 通過schedulerFactory獲取一個調度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 通過schedulerFactory獲取一個調度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 啟動
scheduler.start();
…
//關閉
scheduler.shutdown();
|
Job接口
作用:開發者實現該接口定義需要執行的作業。JobExecutionContext類提供調度上下文的各種信息。
實現Job接口的類還可以使用注解進行修飾。
@DisallowConcurrentExecution:此注解表示不允許這個Job並發執行
@PersistJobDataAfterExecution:此注解表示當這個Job的execute方法執行成功后,更新並存儲它所持有的JobDetail屬性中JobDataMap。如果使用這個注解,強烈建議也使用@DisallowConcurrentExecution,因為並發執行過程中,JobDataMap有可能會發生沖突。
例:
public class xxxJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
…
}
}
|
JobDetail接口
作用:用於定義Job實例。
JobDetail有兩個boolean屬性。
isDurable:如果設為false,則對應的Job一旦沒有關聯的觸發器,就會被Scheduler自動刪除。
requestsRecovery:如果設為true,當Job執行中遇到硬中斷(例如運行崩潰、機器斷電等),Scheduler會重新執行。這種情況下,JobExecutionContext.isRecovering()會返回ture。
JobBuilder類
作用:用於定義、構建JobDetail實例。
例:
// 創建jobDetail實例,綁定Job實現類
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build();
|
Trigger接口
作用:定義Job執行的觸發規則。
Quartz中有多種觸發器,最常用的是SimpleTrigger 和 CronTrigger。
SimpleTrigger一般用於只執行一次或在指定時間執行的作業;CronTrigger一般用於周期性執行(例如,每日執行、每周執行)的作業,需要按照指定的時間表達式規則設置調度時間。
Priority:這個屬性表示Trigger的權重。當兩個Trigger觸發時間相同時,權重大的那個先執行。Quartz默認的權重值為5。
Misfire Instruction:在Trigger接口中可以設置錯過觸發處理機制。就是說在指定觸發的時間點由於某種原因錯過執行的時機了,這時如何去處理。Quartz提供了多種策略,這里不詳述,有興趣的可以參考官方文檔。
Job和Trigger的關系
多個Job可以依賴於一個Trigger;多個Trigger也可以關聯一個Job。
但是,從最佳實踐來看,最好讓Job和Trigger保持一對多的關系,這樣更便於管理。
TriggerBuilder類
作用:用於定義、構建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表達式一節再詳述。
JobDataMap
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();
}
}
|
其他常見API
JobKey 和 TriggerKey
在Quartz中,可以分別通過JobKey和TriggerKey來唯一地識別一個Job或一個Trigger。
這兩個Key都有兩個關鍵屬性:name和group。
CronExpression表達式
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號開始,每隔三天觸發一次。
通配符L
表示最后的意思。
例如在日字段設置上,表示當月的最后一天(依據當前月份,如果是二月還會依據是否是潤年[leap]), 在周字段上表示星期六,相當於"7"或"SAT"。如果在"L"前加上數字,則表示該數據的最后一個。例如在周字段上設置"6L"這樣的格式,則表示“本月最后一個星期五"
通配符W
表示離指定日期的最近那個工作日(周一至周五)。
例如在日字段上設置"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/