Timer
JDK自帶的Timer類,允許調度一個TimerTask任務。
Demo:
/** * Timer測試類 */ public class TimerDemo { public static void main(String[] args) { // 創建定時器 Timer timer = new Timer(); // 添加調度任務 // schedule(TimerTask task, Date time); 特定時間 time 執行 // timer.schedule(new MyTask(), new Date(System.currentTimeMillis() + 1000)); // schedule(TimerTask task, long delay); //延遲 delay毫秒 執行 task // timer.schedule(new MyTask(), 1000); // schedule(TimerTask task, long delay, long period) 延遲 delay毫秒 執行並每隔 period毫秒 執行一次 // timer.schedule(new MyTask(), 1000, 5000); // schedule(TimerTask task, Date time, long period); 特定時間 time 執行並每隔 period毫秒 執行一次 timer.schedule(new MyTask(), new Date(System.currentTimeMillis() + 1000), 5000); } }
/** * 具體執行的任務 */ public class MyTask extends TimerTask { /** * The action to be performed by this timer task. */ public void run() { System.out.println("執行時間:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } }
Spring Task:
Spring3.0以后自主開發的定時任務工具Spring Task,支持線程池,可以高效處理許多不同的定時任務,除spring相關的包外不需要額外的包,支持注解和配置文件兩種形式。 但不能處理過於復雜的任務 。
基於配置的Demo:
定時任務類:
/** * spring執行任務的類 */ public class SpringTask { public void show1() { System.out.println("show1:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } public void show2() { System.out.println("show2:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } }
配置文件spring-schedule.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <bean id="springTask" class="com.zy.springtask.SpringTask"></bean> <!--注冊調度任務--> <task:scheduled-tasks> <!--延遲1秒 執行任務--> <!--<task:scheduled ref="springTask" method="show1" fixed-delay="1000" />--> <!--固定速度3秒 執行任務--> <!--<task:scheduled ref="springTask" method="show2" fixed-rate="3000" />--> <!-- 使用cron表達式 指定觸發時間 spring task 只支持6位的cron表達式 秒 分 時 日 月 星期 --> <task:scheduled ref="springTask" method="show1" cron="1-10 * * ? * *" /> </task:scheduled-tasks> <!--執行器配置--> <task:executor id="threadPoolTaskExecutor" pool-size="10" keep-alive="5"></task:executor> <!--調度器配置--> <task:scheduler id="threadPoolTaskScheduler" pool-size="10"></task:scheduler> </beans>
基於注解的Demo:
定時任務類:
/** * spring執行任務的類 */ @Component public class SpringAnnoTask { @Scheduled(cron = "1-10 * * * * ? ")//每分鍾的1-10秒每秒執行一次 public void show1() { System.out.println("show1:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } @Scheduled(cron = "0/10 * * * * ? ")//每10秒執行一次 public void show2() { System.out.println("show2:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } }
配置文件spring-schedule.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!--啟用注解--> <task:annotation-driven></task:annotation-driven> <bean id="springAnnotationTask" class="com.zy.springtask.SpringAnnoTask"></bean> </beans>
Quartz
這個就厲害了,這篇文章只能簡單介紹個入門案例,如果需要深入研究請自行查看官方文檔或者網上找些系列的文章。w3cschool文檔
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,是完全由java開發的一個開源的任務日程管理系統,“任務進度管理器”就是一個在預先確定(被納入日程)的時間到達時,負責執行(或者通知)其他軟件組件的系統。
特點:
- 強大的調度功能,例如支持豐富多樣的調度方法,可以滿足各種常規及特殊需求;
- 靈活的應用方式,例如支持任務和調度的多種組合方式,支持調度數據的多種存儲方式;
- 分布式和集群能力,Terracotta 收購后在原來功能基礎上作了進一步提升。
- 另外,作為 Spring 默認的調度框架,Quartz 很容易與 Spring 集成實現靈活可配置的調度功能。
核心元素 :
- Scheduler: 任務調度器,是實際執行任務調度的控制器。在spring中通過SchedulerFactoryBean封裝起來。
- Trigger :觸發器,用於定義任務調度的時間規則,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比較多,本文主要介紹這種方式。CronTrigger在spring中封裝在CronTriggerFactoryBean中。
- Calendar:它是一些日歷特定時間點的集合。一個trigger可以包含多個Calendar,以便排除或包含某些時間點。
- Job :任務,是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各種信息。Job運行時的信息保存在JobDataMap實例中。實現Job接口的任務,默認是無狀態的,若要將Job設置成有狀態的,在quartz中是給實現的Job添加@DisallowConcurrentExecution注解(以前是實現StatefulJob接口,現在已被Deprecated),在與spring結合中可以在spring配置文件的job detail中配置concurrent參數。
- JobDetail :任務信息,用來描述Job實現類及其它相關的靜態信息,如Job名字、關聯監聽器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean兩種實現,如果任務調度只需要執行某個類的某個方法,就可以通過MethodInvokingJobDetailFactoryBean來調用。
Trigger觸發器 :
- SimpleTrigger :在一個指定時間段內執行一次作業任務或是在指定時間間隔內執行多次作業任務;
- CronTrigger :基於日歷的作業調度器,而不是像SimpleTrigger那樣精確指定間隔時間,比SimpleTrigger更常用。
簡單案例:
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>
具體定時任務需要執行的類:
/** * 具體執行的任務 實現Job接口 */ public class MyDemoJob implements Job { public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // 當前時間 System.out.println("執行時間:" + sf.format(new Date())); // 獲取Trigger Trigger trigger = jobExecutionContext.getTrigger(); // 通過trigger獲取job標識 JobKey jobKey = trigger.getJobKey(); System.out.println("Job's key:" + "name:" + jobKey.getName() + "\tgroup:" + jobKey.getGroup()); // getClass(); System.out.println("Start time : " + sf.format(trigger.getStartTime())); // System.out.println("End time : " + sf.format(trigger.getEndTime())); } }
基礎Demo:
/** * 基礎觸發器調度程序 */ public class BaseScheduler { public static void main(String[] args) { try { // 1. 創建一個JodDetail實例 將該實例與具體要執行的job類 MyDemoJob.class綁定 JobDetail jobDetail = JobBuilder.newJob(MyDemoJob.class) // 定義Job類為MyDemoJob類(具體執行定時任務的內容) .withIdentity("myJob", "default") // 定義name/group .build(); // 2. 定義一個Trigger,10秒后執行 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); // 2.1 設置開始時間 Date startTime = new Date(); startTime.setTime(startTime.getTime() + 10000L); // 2.2 設置結束時間 // Date endTime = new Date(); //endTime.setTime(endTime.getTime() + 20000L); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "default")// 定義名字和組 .startAt(startTime) //.endAt(endTime) .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()) // 上面這句的意思是 這個定時器每5秒執行一次 直到山峰沒有棱角 河水不再流 詳細使用請查看withSchedule的參數設置 .build(); // 3. 創建scheduler 從StdSchedulerFactory工廠中獲取 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 4. 將jobDetail和trigger加入這個調度(注冊 任務詳情和觸發器) scheduler.scheduleJob(jobDetail, trigger); // 5. 啟動scheduler scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } }
基於SimpleTrigger的Demo:
/** * SimpleTrigger 簡單觸發器調度程序 */ public class SimpleTriggerScheduler { public static void main(String[] args) { try { // 1. 創建一個JodDetail實例 JobDetail jobDetail = JobBuilder.newJob(MyDemoJob.class) .withIdentity("myJob") .build(); // 2. 定義一個Trigger Date startTime = new Date(); startTime.setTime(startTime.getTime() + 5000L); SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger() .withIdentity("myTrigger", "group1")// 定義名字和組 .startAt(startTime) .build(); // 3. 創建scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 4. 將jobDetail和trigger加入這個調度(注冊 任務詳情和觸發器) scheduler.scheduleJob(jobDetail, trigger); // 5. 啟動scheduler scheduler.start(); } catch (Exception e) { e.printStackTrace(); } } }
基於CronTrigger的Demo:
/** * CronTrigger 基於Cron表達式的觸發器調度程序 */ public class CronTriggerScheduler { public static void main(String[] args) { try { // 1. 創建一個JodDetail實例 JobDetail jobDetail = JobBuilder.newJob(MyDemoJob.class) .withIdentity("myJob") // 定義name/group .build(); // 2. 定義一個Trigger 使用Cron表達式來控制運行 CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger() .withIdentity("myTrigger", "group1") .withSchedule( //定義任務調度的時間間隔和次數 CronScheduleBuilder .cronSchedule("0/10 * * * * ? ")//每10秒運行一次 ) .build(); // 3. 創建scheduler SchedulerFactory sfact = new StdSchedulerFactory(); Scheduler scheduler = sfact.getScheduler(); // 4. 將jobDetail和trigger加入這個調度(注冊 任務詳情和觸發器) scheduler.scheduleJob(jobDetail, trigger); // 5. 啟動scheduler scheduler.start(); } catch (Exception e) { e.printStackTrace(); } } }
PS:Cron表達式介紹
(1)Seconds Minutes Hours DayofMonth Month DayofWeek Year
(2)Seconds Minutes Hours DayofMonth Month DayofWeek
各個字段的含義:
字段 | 允許值 | 允許的特殊字符 |
秒(Seconds) | 0~59的整數 | , - * / 四個字符 |
分(Minutes) | 0~59的整數 | , - * / 四個字符 |
小時(Hours) | 0~23的整數 | , - * / 四個字符 |
日期(DayofMonth) | 1~31的整數(但是你需要考慮你月的天數) | ,- * ? / L W C 八個字符 |
月份(Month) | 1~12的整數或者 JAN-DEC | , - * / 四個字符 |
星期(DayofWeek) | 1~7的整數或者 SUN-SAT (1=SUN) | , - * ? / L C # 八個字符 |
年(可選,留空)(Year) | 1970~2099 | , - * / 四個字符 |
每一個域都使用數字,但還可以出現如下特殊字符,它們的含義是:
(1)*:表示匹配該域的任意值。假如在Minutes域使用*, 即表示每分鍾都會觸發事件。
(2)?:只能用在DayofMonth和DayofWeek兩個域。它也匹配域的任意值,但實際不會。因為DayofMonth和DayofWeek會相互影響。
例如想在每月的20日觸發調度,不管20日到底是星期幾,則只能使用如下寫法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期幾都會觸發,實際上並不是這樣。
(3)-:表示范圍。例如在Minutes域使用5-20,表示從5分到20分鍾每分鍾觸發一次
(4)/:表示起始時間開始觸發,然后每隔固定時間觸發一次。例如在Minutes域使用5/20,則意味着5分鍾觸發一次,而25,45等分別觸發一次.
(5),:表示列出枚舉值。例如:在Minutes域使用5,20,則意味着在5和20分每分鍾觸發一次。
(6)L:表示最后,只能出現在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一個星期四觸發。
(7)W:表示有效工作日(周一到周五),只能出現在DayofMonth域,系統將在離指定日期的最近的有效工作日觸發事件。
例如:在 DayofMonth使用5W,如果5日是星期六,則將在最近的工作日:星期五,即4日觸發。如果5日是星期天,則在6日(周一)觸發;
如果5日在星期一到星期五中的一天,則就在5日觸發。另外一點,W的最近尋找不會跨過月份 。
(8)LW:這兩個字符可以連用,表示在某個月最后一個工作日,即最后一個星期五。
(9)#:用於確定每個月第幾個星期幾,只能出現在DayofMonth域。例如在4#2,表示某月的第二個星期三。