在spring boot中教沾,簡(jiǎn)單的任務(wù)調(diào)度工作可以由@Scheduled注解來完成沾瓦。
如果需要深入了解spring的調(diào)度心铃,最好的方式是先看下官方文檔准谚。
一、Spring的任務(wù)抽象接口
TaskExecutor接口
TaskExecutor
是任務(wù)執(zhí)行接口去扣,類似于java.util.concurrent.Executor
柱衔,該接口只有一個(gè)方法execute(Runnable task)
,用于執(zhí)行任務(wù)厅篓。
Spring提供了一組TaskExecutor的實(shí)現(xiàn)秀存,基本上能滿足所有的需求。它們的使用方式跟普通的Spring Bean一樣羽氮。
TaskScheduler接口
TaskScheduler
接口是定時(shí)器的抽象或链,它的源代碼如下〉笛海可以看到澳盐,該接口包含了一組方法用于指定任務(wù)執(zhí)行的時(shí)間。
public interface TaskScheduler {
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Date startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
二令宿、實(shí)操
2.1叼耙、定時(shí)任務(wù)
具體步驟如下:
1. 在應(yīng)用入口增加@EnableScheduling
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String [] args) {
SpringApplication.run(Application.class, args);
}
}
如果應(yīng)用依賴了
spring-boot-starter-actuator
,即使沒有聲明@EnableScheduling粒没,調(diào)度任務(wù)也會(huì)被執(zhí)行筛婉。因?yàn)?code>spring-boot-starter-actuator本身也聲明了@EnableScheduling,見MetricExportAutoConfiguration癞松。
2. 增加任務(wù)調(diào)度服務(wù)
@Component // 或@Service
public class TaskService {
@Scheduled(fixedRate = 1000) // 每隔1秒執(zhí)行一次
public void timerRate() {
// 在此處執(zhí)行調(diào)度任務(wù)爽撒。
}
}
@Scheduled 注解主要使用在執(zhí)行調(diào)度任務(wù)的方法上入蛆。
注意!Springboot 默認(rèn)的執(zhí)行方式是串行執(zhí)行硕勿,無論有多少task(@Scheduled)哨毁,都是一個(gè)線程串行執(zhí)行。同一個(gè)task源武,如果前一個(gè)還沒跑完后面一個(gè)就不會(huì)觸發(fā)扼褪,不同的task也不能同時(shí)運(yùn)行,這是因?yàn)閟cheduler的默認(rèn)線程數(shù)為1的緣故粱栖。
在高可用的情況下话浇,如果微服務(wù)有多個(gè)實(shí)例,scheduler會(huì)在多個(gè)實(shí)例上同時(shí)運(yùn)行查排。
2.2凳枝、并行任務(wù)
在上面的基礎(chǔ)上,增加一個(gè)實(shí)現(xiàn)SchedulingConfigurer接口的類:
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
// 線程前綴
threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
/* @Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(POOL_SIZE); // 線程池初始化10個(gè)線程
}*/
}
如果在調(diào)度方法里打印當(dāng)前線程名稱:
Current Thread : my-scheduled-task-pool-1
Current Thread : my-scheduled-task-pool-2
...
配置多個(gè)線程會(huì)導(dǎo)致同一個(gè)task前一個(gè)還沒跑完后面又被觸發(fā)的問題跋核。
附錄1岖瑰、@Scheduled中的參數(shù)
(1) cron:cron表達(dá)式,指定任務(wù)在特定時(shí)間執(zhí)行;
(2) fixedDelay:表示上一次任務(wù)執(zhí)行完成后多久再次執(zhí)行砂代,參數(shù)類型為long蹋订,單位ms;
(3) fixedDelayString:與fixedDelay含義一樣,只是參數(shù)類型變?yōu)镾tring;
(4) fixedRate:表示按一定的頻率執(zhí)行任務(wù)刻伊,參數(shù)類型為long露戒,單位ms;
(5) fixedRateString: 與fixedRate的含義一樣,只是將參數(shù)類型變?yōu)镾tring;
(6) initialDelay:表示延遲多久再第一次執(zhí)行任務(wù)捶箱,參數(shù)類型為long智什,單位ms;
(7) initialDelayString:與initialDelay的含義一樣,只是將參數(shù)類型變?yōu)镾tring;
(8) zone:時(shí)區(qū)丁屎,默認(rèn)為當(dāng)前時(shí)區(qū)荠锭,一般沒有用到。
例子:
- @Scheduled(fixedRate=1000): 上一次開始執(zhí)行后1秒再次執(zhí)行晨川;
- @Scheduled(fixedDelay=1000):上一次執(zhí)行完成后1秒再次執(zhí)行证九;
- @Scheduled(initialDelay=1000, fixedDelay=3000):第一次延遲1秒執(zhí)行,然后在上一次執(zhí)行完成后3秒再次執(zhí)行共虑;
- @Scheduled(cron="* * * * * ?"):按cron表達(dá)式執(zhí)行愧怜。
附錄2、CRON表達(dá)式
Cron格式
一個(gè) Cron表達(dá)式是由6或7個(gè)字段(年字段是可選字段)的字符串組成妈拌,字段與字段之間用空格來隔開拥坛。
Cron表達(dá)式使用格式:
Seconds | Minutes | Hours | DayofMonth | Month | DayofWeek | [Year] |
---|---|---|---|---|---|---|
秒 | 分 | 時(shí) | 天 | 月 | 周 | [年] |
各個(gè)域的定義:
域 | 必輸 | 含義 | 允許值 | 允許特殊符號(hào) |
---|---|---|---|---|
Seconds | 是 | 秒 | 0-59 | , - * / |
Minutes | 是 | 分 | 0-59 | , - * / |
Hours | 是 | 時(shí) | 0-23 | , - * / |
Day of Month | 是 | 天 | 1-31 | , - * ? / L W |
Month | 是 | 月 | 1-12或者 JAN-DEC | , - * / |
Day of Week | 是 | 周 | 1-7 或者 SUN-SAT | , - * ? / L # |
[Year] | 否 | [年] | 空,1970-2099 | , - * / |
特殊符號(hào)代表的含義:
符號(hào) | 含義 |
---|---|
* | 匹配該域的任意值;如* 用在分 所在的域渴逻,表示每分鐘都會(huì)觸發(fā)事件疾党。 |
? | 無特定值。只能用在DayofMonth 和DayofWeek 兩個(gè)域惨奕。 由于DayofMonth 和DayofWeek 這兩個(gè)元素是互斥的,必須要對(duì)其中一個(gè)設(shè)置? 竭钝。例如想在每月的20日觸發(fā)調(diào)度梨撞,不管20日到底是星期幾,則只能使用如下寫法: 13 13 15 20 * ? , 其中最后一位只能用香罐? 卧波,而不能使用* ,如果使用* 表示不管星期幾都會(huì)觸發(fā)庇茫。 |
- | 匹配一個(gè)特定的范圍值港粱;如時(shí) 所在的域的值是10-12 ,表示10旦签、11查坪、12點(diǎn)的時(shí)候會(huì)觸發(fā)事件. |
, | 匹配多個(gè)指定的值;如周 所在的域的值是2,4,6 宁炫,表示在周一偿曙、周三、周五就會(huì)觸發(fā)事件羔巢。(1表示周日望忆,2表示周一,3表示周二竿秆,以此類推启摄,7表示周六)。 |
/ | 指定數(shù)值的增量幽钢。左邊是開始觸發(fā)時(shí)間歉备,右邊是每隔固定時(shí)間觸發(fā)一次事件,如秒所在的域的值是5/15 搅吁,表示從第5秒開始威创,每15秒觸發(fā),即在第5秒谎懦、20秒肚豺、35秒、50秒的時(shí)候都觸發(fā)一次事件界拦,等價(jià)于“5,20,35,50”吸申。 |
L | last,最后的意思,如果是用在天 這個(gè)域截碴,表示月的最后一天梳侨,如果是用在周所在的域,如6L 日丹,表示某個(gè)月最后一個(gè)周五走哺。 |
W | weekday,工作日的意思哲虾。如天 所在的域的值是15W 丙躏,表示本月15日最近的工作日,如果15日是周六束凑,觸發(fā)器將觸發(fā)上14日周五晒旅。如果15日是周日,觸發(fā)器將觸發(fā)16日周一汪诉。如果15日不是周六或周日废恋,而是周一至周五的某一個(gè),那么它就在15日當(dāng)天觸發(fā)事件。 |
# | 用來指定每個(gè)月的第幾個(gè)星期幾,如6#3 表示某個(gè)月的第三個(gè)星期五运挫。 |
CRON表達(dá)式官方例子
表達(dá)式 | 含義 |
---|---|
"0 0 12 * * ?" | 每天12:00觸發(fā)事件 |
"0 15 10 ? * *" | 每天10:15觸發(fā)事件 |
"0 15 10 * * ?" | 每天10:15觸發(fā)事件 |
"0 15 10 * * ? *" | 每天10:15觸發(fā)事件 |
"0 15 10 * * ? 2005" | 2005年的每天10:15觸發(fā)事件 |
"0 * 14 * * ?" | 每天14點(diǎn)開始觸發(fā),每分鐘觸發(fā)一次蚓哩,14:59分結(jié)束 |
"0 0/5 14 * * ?" | 每天14點(diǎn)開始觸發(fā)到14:59分結(jié)束的每5分鐘觸發(fā)一次事件 |
"0 0/5 14,18 * * ?" | 每天14點(diǎn)開始到14:59期間和18點(diǎn)到18:59期間的每5分鐘觸發(fā)一次事件 |
"0 0-5 14 * * ?" | 每天14點(diǎn)到14:05期間的每1分鐘觸發(fā)一次事件 |
"0 10,44 14 ? 3 WED" | 每年3月的星期三的14:10和14:44觸發(fā)一次事件 |
"0 15 10 ? * MON-FRI" | 周一至周五的10:15觸發(fā)一次事件 |
"0 15 10 15 * ?" | 每月15日10:15觸發(fā)一次事件 |
"0 15 10 L * ?" | 每月最后一日的10:15觸發(fā)一次事件 |
"0 15 10 ? * 6L" | 每月的最后一個(gè)星期五10:15觸發(fā)一次事件 |
"0 15 10 ? * 6L 2002-2005" | 2002年至2005年的每月的最后一個(gè)星期五10:15觸發(fā)一次事件 |
"0 15 10 ? * 6#3" | 每月的第三個(gè)星期五10:15觸發(fā)一次事件 |