springboot實(shí)現(xiàn)定時(shí)任務(wù)的四種方式小結(jié)
因?yàn)槟承┬枨螅谔囟ǖ臅r(shí)間執(zhí)行一些任務(wù),比如定時(shí)刪除服務(wù)器存儲(chǔ)的數(shù)據(jù)緩存,定時(shí)獲取數(shù)據(jù)以及定時(shí)發(fā)送推送等等,這時(shí)就需要用到定時(shí)任務(wù)了。定時(shí)任務(wù),指的是在編程過(guò)程中無(wú)須做復(fù)雜控制的前提下執(zhí)行簡(jiǎn)單的定時(shí)操作。
Timer
在java中一個(gè)完整的定時(shí)任務(wù)可以用Timer和TimerTask兩個(gè)類配合完成。
Timer是一種工具,線程用其安排在后臺(tái)線程中執(zhí)行的任務(wù),可安排任務(wù)執(zhí)行一次或者定期重復(fù)執(zhí)行。
TimerTask是由Timer安排執(zhí)行一次或者重復(fù)執(zhí)行的任務(wù)。
Timer中提供了四個(gè)方法:
(1)schedule(TimerTask task,Date time)——安排在指定的時(shí)間執(zhí)行指定的任務(wù)
(2)schedule(TimerTask task,Date firstTime,long period)——安排指定的任務(wù)在指定的時(shí)間開始進(jìn)行重復(fù)的固定延遲執(zhí)行
(3)schedule(TimerTask task,long delay)——安排在指定延遲后執(zhí)行指定的任務(wù)
(4)schedule(TimerTask task,long delay,long period)——安排指定的任務(wù)在指定的延遲后開始進(jìn)行重復(fù)的固定速率執(zhí)行
示例:
首先需要?jiǎng)?chuàng)建一個(gè)類作為定時(shí)任務(wù),該類需要繼承TimerTask
public class TimerTask extends java.util.TimerTask{? ?? ?@Override ?? ?public void run() { ?? ??? ?//這里執(zhí)行定時(shí)任務(wù)內(nèi)容 ?? ??? ?System.out.println("+++++++"); ?? ?}?? ? }
然后創(chuàng)建Timer調(diào)用之前創(chuàng)建的定時(shí)任務(wù)
public class TimerTest { ?? ? ?? ?public static void main(String[] args) { ?? ??? ?Timer timer = new Timer(); ?? ??? ?TimerTask noticeTask = new TimerTask(); ?? ??? ?timer.schedule(noticeTask,0,2000); ?? ??? ?timer.cancel(); ?? ??? ?System.out.println("結(jié)束");?? ? ?? ?} ? }
這樣定時(shí)執(zhí)行任務(wù)的功能就實(shí)現(xiàn)了,但Timer有著一定的缺陷:
Timer對(duì)于系統(tǒng)時(shí)間的改變非常敏感,它對(duì)調(diào)度的支持是基于絕對(duì)時(shí)間而不是相對(duì)時(shí)間。
Timer線程是不會(huì)捕獲異常的,多線程并行處理定時(shí)任務(wù)時(shí),Timer運(yùn)行多個(gè)TimerTask時(shí),只要其中之一沒有捕獲拋出的異常,其他任務(wù)便會(huì)自動(dòng)終止運(yùn)行。同時(shí)Timer也不會(huì)重新恢復(fù)線程的執(zhí)行,它會(huì)錯(cuò)誤的認(rèn)為整個(gè)Timer線程都會(huì)取消,已經(jīng)被安排但尚未執(zhí)行的TimerTask也不會(huì)再執(zhí)行了,新的任務(wù)也不能被調(diào)度。因此,如果TimerTask拋出未檢查的異常,Timer將會(huì)產(chǎn)生無(wú)法預(yù)料的行為。
ScheduledExecutor
Timer是基于絕對(duì)時(shí)間的,對(duì)系統(tǒng)時(shí)間比較敏感,而ScheduledExecutor則是基于相對(duì)時(shí)間。
Timer的內(nèi)部只有一個(gè)線程,如果有多個(gè)任務(wù)的話就會(huì)順序執(zhí)行,這樣我們的延遲時(shí)間和循環(huán)時(shí)間就會(huì)出現(xiàn)問(wèn)題。而ScheduledThreadPoolExecutor內(nèi)部是個(gè)線程池,可以支持多個(gè)任務(wù)并發(fā)執(zhí)行,在對(duì)延遲任務(wù)和循環(huán)任務(wù)要求嚴(yán)格的時(shí)候,就需要考慮使用ScheduledExecutor了。
針對(duì)Timer類存在的缺陷,Java 5 推出了基于線程池設(shè)計(jì)的 ScheduledExecutor,ScheduledExecutor的設(shè)計(jì)思想是每一個(gè)被調(diào)度的任務(wù)都會(huì)由線程池中一個(gè)線程去執(zhí)行,因此任務(wù)是并發(fā)的,相互之間不會(huì)受到干擾,只有當(dāng)任務(wù)的時(shí)間到來(lái)時(shí),ScheduledExecutor才會(huì)真正啟動(dòng)一個(gè)線程,其余時(shí)間ScheduledExecutor都是處于輪詢?nèi)蝿?wù)的狀態(tài)。如果我們?cè)O(shè)定的調(diào)度周期小于任務(wù)運(yùn)行時(shí)間,該任務(wù)會(huì)被重復(fù)添加到一個(gè)延時(shí)任務(wù)隊(duì)列,所以同一時(shí)間任務(wù)隊(duì)列中會(huì)有多個(gè)任務(wù)待調(diào)度,線程池會(huì)首先獲取優(yōu)先級(jí)高的任務(wù)執(zhí)行。效果就是任務(wù)運(yùn)行多長(zhǎng)時(shí)間,調(diào)度時(shí)間就會(huì)變?yōu)槎嗑?,因?yàn)樘砑拥饺蝿?wù)隊(duì)列的任務(wù)的延時(shí)時(shí)間每次都是負(fù)數(shù),所以會(huì)被立刻執(zhí)行。
示例:
public class MyScheduledExecutor implements Runnable{ ?? ? ? ? private String jobName; ? ?? ? ? MyScheduledExecutor() { ? ? ? ?? ? ? } ? ?? ? ? MyScheduledExecutor(String jobName) { ? ? ? ? this.jobName = jobName; ? ? } ? ? ? @Override ? ? public void run() { ?? ? ? ?System.out.println(jobName + " is running"); ? ? } ? }
public class MyScheduledExecutorService { public static void main(String[] args) { long initialDelay = 3; long period = 1; /** * 創(chuàng)建一個(gè)線程池,它可安排在給定延遲后運(yùn)行任務(wù)或者定期地執(zhí)行任務(wù) * 參數(shù):corePoolSize - 池中所保存的線程數(shù),即使線程是空閑的也包括在內(nèi) */ ScheduledExecutorService service = Executors.newScheduledThreadPool(2); /** * 從現(xiàn)在開始3秒鐘之后,每隔1秒鐘執(zhí)行一次job1,ScheduleAtFixedRate是基于固定時(shí)間間隔進(jìn)行任務(wù)調(diào)度 * 參數(shù):1、任務(wù)體 2、首次執(zhí)行的延時(shí)時(shí)間 * 3、任務(wù)執(zhí)行間隔 4、間隔時(shí)間單位 */ service.scheduleAtFixedRate(new MyScheduledExecutor("job1"), initialDelay, period, TimeUnit.SECONDS); /** * 從現(xiàn)在開始3秒鐘之后,每隔1秒鐘執(zhí)行一次job2,ScheduleWithFixedDelay 取決于每次任務(wù)執(zhí)行的時(shí)間長(zhǎng)短,基于不固定時(shí)間間隔進(jìn)行任務(wù)調(diào)度 */ service.scheduleWithFixedDelay(new MyScheduledExecutor("job2"), initialDelay, period, TimeUnit.SECONDS); } }
ScheduledExecutor 配合 Calendar 實(shí)現(xiàn)復(fù)雜任務(wù)調(diào)度
示例:設(shè)置每星期二的 18:30:00 執(zhí)行任務(wù)
使用 ScheduledExcetuor 和 Calendar 進(jìn)行任務(wù)調(diào)度
public class ScheduledExceutorTest2 extends TimerTask { ? ?? ?private String jobName = ""; ? ?? ?public ScheduledExceutorTest2(String jobName) { ?? ??? ?super(); ?? ??? ?this.jobName = jobName; ?? ?} ? ?? ?@Override ?? ?public void run() { ?? ??? ?System.out.println("Date = " + new Date() + ", execute " + jobName); ?? ?} ? ?? ?/** ?? ? * 計(jì)算從當(dāng)前時(shí)間currentDate開始,滿足條件dayOfWeek, hourOfDay, minuteOfHour, ?? ? * secondOfMinite的最近時(shí)間 ?? ? */ ?? ?public Calendar getEarliestDate(Calendar currentDate, int dayOfWeek, int hourOfDay, int minuteOfHour, ?? ??? ??? ?int secondOfMinite) { ?? ??? ?// 計(jì)算當(dāng)前時(shí)間的WEEK_OF_YEAR,DAY_OF_WEEK, HOUR_OF_DAY, MINUTE,SECOND等各個(gè)字段值 ?? ??? ?int currentWeekOfYear = currentDate.get(Calendar.WEEK_OF_YEAR); ?? ??? ?int currentDayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK); ?? ??? ?int currentHour = currentDate.get(Calendar.HOUR_OF_DAY); ?? ??? ?int currentMinute = currentDate.get(Calendar.MINUTE); ?? ??? ?int currentSecond = currentDate.get(Calendar.SECOND); ?? ??? ?// 如果輸入條件中的dayOfWeek小于當(dāng)前日期的dayOfWeek,則WEEK_OF_YEAR需要推遲一周 ?? ??? ?boolean weekLater = false; ?? ??? ?if (dayOfWeek < currentDayOfWeek) { ?? ??? ??? ?weekLater = true; ?? ??? ?} else if (dayOfWeek == currentDayOfWeek) { ?? ??? ??? ?// 當(dāng)輸入條件與當(dāng)前日期的dayOfWeek相等時(shí),如果輸入條件中的 ?? ??? ??? ?// hourOfDay小于當(dāng)前日期的 ?? ??? ??? ?// currentHour,則WEEK_OF_YEAR需要推遲一周 ?? ??? ??? ?if (hourOfDay < currentHour) { ?? ??? ??? ??? ?weekLater = true; ?? ??? ??? ?} else if (hourOfDay == currentHour) { ?? ??? ??? ??? ?// 當(dāng)輸入條件與當(dāng)前日期的dayOfWeek, hourOfDay相等時(shí), ?? ??? ??? ??? ?// 如果輸入條件中的minuteOfHour小于當(dāng)前日期的 ?? ??? ??? ??? ?// currentMinute,則WEEK_OF_YEAR需要推遲一周 ?? ??? ??? ??? ?if (minuteOfHour < currentMinute) { ?? ??? ??? ??? ??? ?weekLater = true; ?? ??? ??? ??? ?} else if (minuteOfHour == currentSecond) { ?? ??? ??? ??? ??? ?// 當(dāng)輸入條件與當(dāng)前日期的dayOfWeek, hourOfDay, ?? ??? ??? ??? ??? ?// minuteOfHour相等時(shí),如果輸入條件中的 ?? ??? ??? ??? ??? ?// secondOfMinite小于當(dāng)前日期的currentSecond, ?? ??? ??? ??? ??? ?// 則WEEK_OF_YEAR需要推遲一周 ?? ??? ??? ??? ??? ?if (secondOfMinite < currentSecond) { ?? ??? ??? ??? ??? ??? ?weekLater = true; ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ?} ?? ??? ?if (weekLater) { ?? ??? ??? ?// 設(shè)置當(dāng)前日期中的WEEK_OF_YEAR為當(dāng)前周推遲一周 ?? ??? ??? ?currentDate.set(Calendar.WEEK_OF_YEAR, currentWeekOfYear + 1); ?? ??? ?} ?? ??? ?// 設(shè)置當(dāng)前日期中的DAY_OF_WEEK,HOUR_OF_DAY,MINUTE,SECOND為輸入條件中的值。 ?? ??? ?currentDate.set(Calendar.DAY_OF_WEEK, dayOfWeek); ?? ??? ?currentDate.set(Calendar.HOUR_OF_DAY, hourOfDay); ?? ??? ?currentDate.set(Calendar.MINUTE, minuteOfHour); ?? ??? ?currentDate.set(Calendar.SECOND, secondOfMinite); ?? ??? ?return currentDate; ?? ?} ? ?? ?public static void main(String[] args) throws Exception { ?? ??? ?ScheduledExceutorTest2 test = new ScheduledExceutorTest2("job1"); ?? ??? ?// 獲取當(dāng)前時(shí)間 ?? ??? ?Calendar currentDate = Calendar.getInstance(); ?? ??? ?long currentDateLong = currentDate.getTime().getTime(); ?? ??? ?System.out.println("Current Date = " + currentDate.getTime().toString()); ?? ??? ?// 計(jì)算滿足條件的最近一次執(zhí)行時(shí)間 ?? ??? ?Calendar earliestDate = test.getEarliestDate(currentDate, 3, 18, 30, 00); ?? ??? ?long earliestDateLong = earliestDate.getTime().getTime(); ?? ??? ?System.out.println("Earliest Date = " + earliestDate.getTime().toString()); ?? ??? ?// 計(jì)算從當(dāng)前時(shí)間到最近一次執(zhí)行時(shí)間的時(shí)間間隔 ?? ??? ?long delay = earliestDateLong - currentDateLong; ?? ??? ?// 計(jì)算執(zhí)行周期為一星期 ?? ??? ?long period = 7 * 24 * 60 * 60 * 1000; ?? ??? ?ScheduledExecutorService service = Executors.newScheduledThreadPool(10); ?? ??? ?// 從現(xiàn)在開始delay毫秒之后,每隔一星期執(zhí)行一次job1 ?? ??? ?service.scheduleAtFixedRate(test, delay, period, TimeUnit.MILLISECONDS); ?? ?} ? }
其核心在于根據(jù)當(dāng)前時(shí)間推算出最近一個(gè)星期二 18:30:00 的絕對(duì)時(shí)間,然后計(jì)算與當(dāng)前時(shí)間的時(shí)間差,作為調(diào)用 ScheduledExceutor 函數(shù)的參數(shù),計(jì)算最近時(shí)間要用到 java.util.calendar 的功能。
注解@Scheduled
Spring提供的注解,優(yōu)點(diǎn)就是配置簡(jiǎn)單,依賴少,缺點(diǎn)是同一個(gè)task,如果前一個(gè)還沒跑完后面一個(gè)就不會(huì)觸發(fā),不同的task也不能同時(shí)運(yùn)行。因?yàn)閟cheduler的默認(rèn)線程數(shù)為1,配置pool-size為2的話,會(huì)導(dǎo)致同一個(gè)task前一個(gè)還沒跑完后面又被觸發(fā)的問(wèn)題,不支持集群等。
示例:
yml文件配置:
time: ? ? cron: 0/5 * * * * * ? ? interval: 5
啟動(dòng)類添加@EnableScheduling
定時(shí)任務(wù):
@Component public class TimeTask { ?? ? ?? ?@Scheduled(cron = "${time.cron}") ?? ?public void ?flush1() throws ?Exception{ ? ? ? ? ? ? System.out.println("Execute1"); ? ? ? ? } ?? ? ?? ?@Scheduled(cron = "0/${time.interval} * * * * ?") ?? ?public void ?flush2() throws ?Exception{ ? ? ? ? ? ? System.out.println("Execute2"); ? ? ? ? } ?? ? ?? ?@Scheduled(cron = "0/5 * * * * ?") ?? ?public void ?flush3() throws ?Exception{ ? ? ? ? ? ? System.out.println("Execute3"); ? ? ? ? } ? }
參數(shù)介紹:
fixedDelay
上一次執(zhí)行完畢時(shí)間點(diǎn)之后多長(zhǎng)時(shí)間再執(zhí)行。如:
@Scheduled(fixedDelay = 5000) //上一次執(zhí)行完畢時(shí)間點(diǎn)之后5秒再執(zhí)行
fixedDelayString
與 fixedDelay 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。如:
@Scheduled(fixedDelayString = "5000") //上一次執(zhí)行完畢時(shí)間點(diǎn)之后5秒再執(zhí)行
占位符的使用:
@Scheduled(fixedDelayString = "${time.fixedDelay}") void testFixedDelayString() { System.out.println("Execute”); }
fixedRate
上一次開始執(zhí)行時(shí)間點(diǎn)之后多長(zhǎng)時(shí)間再執(zhí)行。如:
@Scheduled(fixedRate = 5000) //上一次開始執(zhí)行時(shí)間點(diǎn)之后5秒再執(zhí)行
fixedRateString
與 fixedRate 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。
initialDelay
第一次延遲多長(zhǎng)時(shí)間后再執(zhí)行。如:
@Scheduled(initialDelay=1000, fixedRate=5000) //第一次延遲1秒后執(zhí)行,之后按fixedRate的規(guī)則每5秒執(zhí)行一次
initialDelayString
與 initialDelayString 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。
cron表達(dá)式
cron表達(dá)式是一個(gè)字符串,字符串以5或6個(gè)空格隔開,分成6或7個(gè)域,每一個(gè)域代表一個(gè)含義。
cron表達(dá)式語(yǔ)法:
[秒] [分] [小時(shí)] [日] [月] [周] [年]
注:[年]不是必須的域,可以省略[年],則一共6個(gè)域
其中各個(gè)域的定義如下:
序號(hào) | 說(shuō)明 | 必填 | 允許填寫的值 | 允許的通配符 |
1 | 秒 | 是 | 0-59 | , - * / |
2 | 分 | 是 | 0-59 | , - * / |
3 | 時(shí) | 是 | 0-23 | , - * / |
4 | 日 | 是 | 1月31日 | , - * ? / L W |
5 | 月 | 是 | 1-12 / JAN-DEC | , - * / |
6 | 周 | 是 | 1-7 / SUN-SAT | , - * ? / L # |
7 | 年 | 否 | 1970-2099 | , - * / |
Cron表達(dá)式對(duì)特殊字符的大小寫不敏感,對(duì)代表星期的縮寫英文大小寫也不敏感。星號(hào)(*):可用在所有字段中,表示對(duì)應(yīng)時(shí)間域的每一個(gè)時(shí)刻,例如, 在分鐘字段時(shí),表示“每分鐘”;
問(wèn)號(hào)(?):該字符只在日期和星期字段中使用,它通常指定為“無(wú)意義的值”,相當(dāng)于點(diǎn)位符;
- 減號(hào)(-):表達(dá)一個(gè)范圍,如在小時(shí)字段中使用“10-12”,則表示從10到12點(diǎn),即10,11,12;
- 逗號(hào)(,):表達(dá)一個(gè)列表值,如在星期字段中使用“MON,WED,FRI”,則表示星期一,星期三和星期五;
- 斜杠(/):x/y表達(dá)一個(gè)等步長(zhǎng)序列,x為起始值,y為增量步長(zhǎng)值。如在分鐘字段中使用0/15,則表示為0,15,30和45秒,而5/15在分鐘字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
- L:該字符只在日期和星期字段中使用,代表“Last”的意思,但它在兩個(gè)字段中意思不同。L在日期字段中,表示這個(gè)月份的最后一天,如一月的31號(hào),非閏年二月的28號(hào);如果L用在星期中,則表示星期六,等同于7。但是,如果L出現(xiàn)在星期字段里,而且在前面有一個(gè)數(shù)值X,則表示“這個(gè)月的最后X天”,例如,6L表示該月的最后星期五;
- W:該字符只能出現(xiàn)在日期字段里,是對(duì)前導(dǎo)日期的修飾,表示離該日期最近的工作日。例如15W表示離該月15號(hào)最近的工作日,如果該月15號(hào)是星期六,則匹配14號(hào)星期五;如果15日是星期日,則匹配16號(hào)星期一;如果15號(hào)是星期二,那結(jié)果就是15號(hào)星期二。但必須注意關(guān)聯(lián)的匹配日期不能夠跨月,如你指定1W,如果1號(hào)是星期六,結(jié)果匹配的是3號(hào)星期一,而非上個(gè)月最后的那天。W字符串只能指定單一日期,而不能指定日期范圍;
- LW組合:在日期字段可以組合使用LW,它的意思是當(dāng)月的最后一個(gè)工作日;
- 井號(hào)(#):該字符只能在星期字段中使用,表示當(dāng)月某個(gè)工作日。如6#3表示當(dāng)月的第三個(gè)星期五(6表示星期五,#3表示當(dāng)前的第三個(gè)),而4#5表示當(dāng)月的第五個(gè)星期三,假設(shè)當(dāng)月沒有第五個(gè)星期三,忽略不觸發(fā);
- C:該字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是計(jì)劃所關(guān)聯(lián)的日期,如果日期沒有被關(guān)聯(lián),則相當(dāng)于日歷中所有日期。例如5C在日期字段中就相當(dāng)于日歷5日以后的第一天。1C在星期字段中相當(dāng)于星期日后的第一天。
示例:
表示式 | 說(shuō)明 |
0 0 12 * * ? | 每天12點(diǎn)運(yùn)行 |
0 15 10 ? * * | 每天10:15運(yùn)行 |
0 15 10 * * ? | 每天10:15運(yùn)行 |
0 15 10 * * ? * | 每天10:15運(yùn)行 |
0 15 10 * * ? 2008 | 在2008年的每天10:15運(yùn)行 |
0 * 14 * * ? | 每天14點(diǎn)到15點(diǎn)之間每分鐘運(yùn)行一次,開始于14:00,結(jié)束于14:59。 |
0 0/5 14 * * ? | 每天14點(diǎn)到15點(diǎn)每5分鐘運(yùn)行一次,開始于14:00,結(jié)束于14:55。 |
0 0/5 14,18 * * ? | 每天14點(diǎn)到15點(diǎn)每5分鐘運(yùn)行一次,此外每天18點(diǎn)到19點(diǎn)每5鐘也運(yùn)行一次。 |
0 0-5 14 * * ? | 每天14:00點(diǎn)到14:05,每分鐘運(yùn)行一次。 |
0 10,44 14 ? 3 WED | 3月每周三的14:10分到14:44,每分鐘運(yùn)行一次。 |
0 15 10 ? * MON-FRI | 每周一,二,三,四,五的10:15分運(yùn)行。 |
0 15 10 15 * ? | 每月15日10:15分運(yùn)行。 |
0 15 10 L * ? | 每月最后一天10:15分運(yùn)行。 |
0 15 10 ? * 6L | 每月最后一個(gè)星期五10:15分運(yùn)行。 |
0 15 10 ? * 6L 2007-2009 | 在2007,2008,2009年每個(gè)月的最后一個(gè)星期五的10:15分運(yùn)行。 |
0 15 10 ? * 6#3 | 每月第三個(gè)星期五的10:15分運(yùn)行。 |
Quartz
Quartz 是一個(gè)完全由 Java 編寫的開源作業(yè)調(diào)度框架,它可以集成在幾乎任何Java應(yīng)用程序中進(jìn)行作業(yè)調(diào)度。
Quartz 可以與 J2EE 與 J2SE 應(yīng)用程序相結(jié)合也可以單獨(dú)使用。
Quartz 允許程序開發(fā)人員根據(jù)時(shí)間的間隔來(lái)調(diào)度作業(yè)。
Quartz 實(shí)現(xiàn)了作業(yè)和觸發(fā)器的多對(duì)多的關(guān)系,還能把多個(gè)作業(yè)與不同的觸發(fā)器關(guān)聯(lián)。
Quartz的運(yùn)行環(huán)境
Quartz 可以運(yùn)行嵌入在另一個(gè)獨(dú)立式應(yīng)用程序。
Quartz 可以在應(yīng)用程序服務(wù)器(或 servlet 容器)內(nèi)被實(shí)例化,并且參與 XA 事務(wù)。
Quartz 可以作為一個(gè)獨(dú)立的程序運(yùn)行(其自己的 Java 虛擬機(jī)內(nèi)),可以通過(guò) RMI 使用。
Quartz 可以被實(shí)例化,作為獨(dú)立的項(xiàng)目集群(負(fù)載平衡和故障轉(zhuǎn)移功能),用于作業(yè)的執(zhí)行。
Job
代表一個(gè)工作,要執(zhí)行的具體內(nèi)容。此接口中只有一個(gè)方法,如下:
public class QuartzJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println(new Date()); } }
可以通過(guò)實(shí)現(xiàn)該接口來(lái)定義需要執(zhí)行的任務(wù)。
JobDetail
用于定義作業(yè)的實(shí)例,代表一個(gè)具體的可執(zhí)行的調(diào)度程序,Job 是這個(gè)可執(zhí)行程調(diào)度程序所要執(zhí)行的內(nèi)容,另外 JobDetail 還包含了這個(gè)任務(wù)調(diào)度的方案和策略。
Trigger
代表一個(gè)調(diào)度參數(shù)的配置,什么時(shí)候去調(diào)。
1> SimpleTrigger:在某個(gè)時(shí)間段內(nèi)實(shí)現(xiàn)定時(shí)任務(wù)的重復(fù)執(zhí)行。
參數(shù):startTime(開始時(shí)間)、endTime(結(jié)束時(shí)間)、repeatCount(重復(fù)數(shù)次)、repeatInterval(重復(fù)執(zhí)行間隔)
2> CronTrigger:基于日歷的概念執(zhí)行計(jì)劃,這個(gè)trigger是最常用的。
參數(shù):startTime(開始時(shí)間)、endTime(結(jié)束時(shí)間)、cronExpression(定時(shí)表達(dá)式)、timeZone(時(shí)區(qū),默認(rèn)獲取jvm所在時(shí)區(qū))
Scheduler
代表一個(gè)調(diào)度容器,一個(gè)調(diào)度容器中可以注冊(cè)多個(gè) JobDetail 和 Trigger。當(dāng) Trigger 與 JobDetail 組合,就可以被 Scheduler 容器調(diào)度了。
Calendar
是一些日歷特定時(shí)間的集合。一個(gè)Trigger可以和多個(gè)calendar關(guān)聯(lián),可以通過(guò)calendar在指定時(shí)間不執(zhí)行任務(wù)。
示例:服務(wù)啟動(dòng)5秒后執(zhí)行,任務(wù)間隔2秒,服務(wù)啟動(dòng)15秒后關(guān)閉
依賴
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency>
任務(wù)調(diào)度
@Component //SpringBoot服務(wù)啟動(dòng)執(zhí)行 public class CronScheduler implements CommandLineRunner { @Override public void run(String... args) throws Exception { JobDetail build = JobBuilder.newJob(QuartzJob.class) .withIdentity("myJob", "group1") .build(); Date date = new Date(); long startTime = date.getTime() + 5000; long endTime = date.getTime() + 15000; CronTrigger c = TriggerBuilder.newTrigger() .startAt(new Date(startTime)) .endAt(new Date(endTime)) .withIdentity("CronTrigger1", "t1") .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) .build(); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //設(shè)置調(diào)度的job和trigger scheduler.scheduleJob(build, c); //開啟調(diào)度 scheduler.start(); //暫停,可以重新啟動(dòng) //scheduler.standby(); //停止調(diào)度程序觸發(fā)觸發(fā)器,并清除與調(diào)度程序關(guān)聯(lián)的所有資源 //scheduler.shushutdown(); } }
具體執(zhí)行的任務(wù)
public class QuartzJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println(new Date()); } }
到此這篇關(guān)于springboot實(shí)現(xiàn)定時(shí)任務(wù)的四種方式小結(jié)的文章就介紹到這了,更多相關(guān)springboot 定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring的@RequestParam對(duì)象綁定方式
這篇文章主要介紹了Spring的@RequestParam對(duì)象綁定方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Spring Aop 如何獲取參數(shù)名參數(shù)值
這篇文章主要介紹了Spring Aop 如何獲取參數(shù)名參數(shù)值的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07利用openoffice+jodconverter-code-3.0-bate4實(shí)現(xiàn)ppt轉(zhuǎn)圖片
這篇文章主要為大家詳細(xì)介紹了利用openoffice+jodconverter-code-3.0-bate4實(shí)現(xiàn)ppt轉(zhuǎn)圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07JAVA 16位ID生成工具類含16位不重復(fù)的隨機(jī)數(shù)數(shù)字+大小寫
這篇文章主要介紹了JAVA 16位ID生成工具類含16位不重復(fù)的隨機(jī)數(shù)數(shù)字+大小寫,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02springboot @ComponentScan注解原理解析
這篇文章主要介紹了springboot @ComponentScan注解原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02