前言
定時(shí)任務(wù) 一般會(huì)存在 中大型企業(yè)級 項(xiàng)目中,為了減少 服務(wù)器瞻想、數(shù)據(jù)庫 的壓力咆耿,往往會(huì)以 定時(shí)任務(wù) 的方式去完成某些業(yè)務(wù)邏輯。
常見的就是 金融服務(wù)系統(tǒng) 推送回調(diào)故硅,一般支付系統(tǒng)訂單在沒有收到成功的回調(diào)返回內(nèi)容時(shí)會(huì) 持續(xù)性的回調(diào)外遇,這種回調(diào)一般都是 定時(shí)任務(wù) 來完成。
還有就是 報(bào)表的生成契吉,我們一般會(huì)在客戶 訪問量小 時(shí)完成這個(gè)操作跳仿,也可以采用 定時(shí)任務(wù) 來完成。
正文
定時(shí)任務(wù)的幾種方式
Timer
這是 Java
自帶的 java.util.Timer
類捐晶,這個(gè)類允許調(diào)度一個(gè)名為 java.util.TimerTask
任務(wù)菲语。使用這種方式可以讓你的程序按照某一個(gè) 頻度 執(zhí)行妄辩,但不能在 指定時(shí)間 運(yùn)行。現(xiàn)在一般用的較少山上。
ScheduledExecutorService
JDK
自帶的一個(gè)類眼耀,是基于 線程池 設(shè)計(jì)的定時(shí)任務(wù)類,每個(gè) 調(diào)度任務(wù) 都會(huì)分配到 線程池 中的一個(gè) 線程 去執(zhí)行佩憾。也就是說哮伟,任務(wù)是 并發(fā)執(zhí)行,互不影響的妄帘。
Spring Task
Spring 3.0
以后自帶的 Task
楞黄,支持 多線程 調(diào)度,可以將它看成一個(gè) 輕量級 的 Quartz
抡驼,而且使用起來比 Quartz
簡單許多鬼廓,但是適用于 單節(jié)點(diǎn) 的 定時(shí)任務(wù)調(diào)度。
Quartz
這是一個(gè) 功能比較強(qiáng)大 的的調(diào)度器致盟,可以讓你的程序在指定時(shí)間執(zhí)行碎税,也可以按照某一個(gè)頻度執(zhí)行,配置起來 稍顯復(fù)雜馏锡。Quartz
功能強(qiáng)大雷蹂,可以結(jié)合 數(shù)據(jù)庫 做 持久化,進(jìn)行 分布式 的 任務(wù)延時(shí)調(diào)度杯道。
Cron表達(dá)式簡介
Cron
表達(dá)式是一個(gè)字符串匪煌,字符串以 5
或 6
個(gè) 空格 隔開,分為 6
或 7
個(gè) 域蕉饼,每一個(gè)域代表一個(gè)含義虐杯,Cron
有如下兩種語法格式:
- Seconds Minutes Hours DayofMonth Month DayofWeek Year
- Seconds Minutes Hours DayofMonth Month DayofWeek
每個(gè)域?qū)?yīng)的含義、域值范圍和特殊表示符昧港,從左到右依次如下:
字段 | 允許值 | 允許的特殊字符 |
---|---|---|
秒 | 0-59 | , - * / |
分 | 0-59 | , - * / |
小時(shí) | 0-23 | , - * / |
日期 | 1-31 | , - * / L W C |
月份 | 1-12 或者 JAN-DEC | , - * / |
星期 | 1-7 或者 SUN-SAT | , - * / L C # |
年(可選) | 留空, 1970-2099 | , - * / |
如上面的表達(dá)式所示:
""字符: 被用來指定所有的值擎椰。如:在分鐘的字段域里表示"每分鐘"。
"-"字符: 被用來指定一個(gè)范圍创肥。如:"10-12" 在小時(shí)域意味著 "10點(diǎn)达舒、11點(diǎn)、12點(diǎn)"叹侄。
","字符: 被用來指定另外的值巩搏。如:"MON,WED,FRI" 在星期域里表示 "星期一、星期三趾代、星期五"贯底。
"?"字符: 只在日期域和星期域中使用。它被用來指定"非明確的值"撒强。當(dāng)你需要通過在這兩個(gè)域中的一個(gè)來指定一些東西的時(shí)候禽捆,它是有用的笙什。看下面的例子你就會(huì)明白胚想。
-
"L"字符: 指定在月或者星期中的某天(最后一天)琐凭。即 "Last" 的縮寫。但是在星期和月中 "L" 表示不同的意思浊服,如:在月子段中 "L" 指月份的最后一天 - 1月31日统屈,2月28日。
- 如果在星期字段中則簡單的表示為 "7" 或者 "SAT" 字符牙躺。
- 如果在星期字段中在某個(gè) value 值得后面愁憔,則表示 "某月的最后一個(gè)星期value",如 "6L" 表示某月的最后一個(gè)星期五述呐。
"W"字符: 只能用在月份字段中惩淳,該字段指定了離指定日期最近的那個(gè)星期日蕉毯。
"#"字符: 只能用在星期字段乓搬,該字段指定了第幾個(gè)星期 value 在某月中
每一個(gè)元素都可以顯式地規(guī)定一個(gè)值(如 6
),一個(gè)區(qū)間(如 9-12
)代虾,一個(gè)列表(如 9进肯,11,13
)或一個(gè)通配符(如 *
)棉磨。"月份中的日期" 和 "星期中的日期" 這兩個(gè)元素是 互斥的江掩,因此應(yīng)該通過設(shè)置一個(gè) 問號(?
)來表明你不想設(shè)置的那個(gè)字段。下表顯示了一些 cron
表達(dá)式的 例子 和它們的意義:
表達(dá)式 | 意義 |
---|---|
"0 0 12 * * ?" | 每天中午12點(diǎn)觸發(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 * * ?" | 在每天下午2點(diǎn)到下午2:59期間的每1分鐘觸發(fā) |
"0 0/5 14 * * ?" | 在每天下午2點(diǎn)到下午2:55期間的每5分鐘觸發(fā) |
"0 0/5 14,18 * * ?" | 在每天下午2點(diǎn)到2:55期間和下午6點(diǎn)到6:55期間的每5分鐘觸發(fā) |
"0 0-5 14 * * ?" | 在每天下午2點(diǎn)到下午2:05期間的每1分鐘觸發(fā) |
"0 10,44 14 ? 3 WED" | 每年三月的星期三的下午2:10和2: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ā) |
0 6 * * * | 每天早上6點(diǎn) |
0 /2 * * | 每兩個(gè)小時(shí) |
0 23-7/2乘瓤,8 * * * | 晚上11點(diǎn)到早上8點(diǎn)之間每兩個(gè)小時(shí)环形,早上八點(diǎn) |
0 11 4 * 1-3 | 每個(gè)月的4號和每個(gè)禮拜的禮拜一到禮拜三的早上11點(diǎn) |
0 4 1 1 * | 1月1日早上4點(diǎn) |
環(huán)境準(zhǔn)備
配置gradle依賴
利用 Spring Initializer
創(chuàng)建一個(gè) gradle
項(xiàng)目 spring-boot-scheduler-task-management
,創(chuàng)建時(shí)添加相關(guān)依賴衙傀。得到的初始 build.gradle
如下:
buildscript {
ext {
springBootVersion = '2.0.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'io.ostenant.springboot.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
在 Spring Boot
入口類上配置 @EnableScheduling
注解開啟 Spring
自帶的定時(shí)處理功能抬吟。
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
配置Timer任務(wù)
這個(gè) API
目前在項(xiàng)目中很少用,直接給出示例代碼统抬。具體的介紹可以查看 API
火本。Timer
的內(nèi)部只有 一個(gè)線程,如果有 多個(gè)任務(wù) 的話就會(huì) 順序執(zhí)行聪建,這樣任務(wù)的 延遲時(shí)間 和 循環(huán)時(shí)間 就會(huì)出現(xiàn)問題钙畔。
TimerService.java
public class TimerService {
private static final Logger LOGGER = LoggerFactory.getLogger(TimerService.class);
private AtomicLong counter = new AtomicLong();
public void schedule() {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
long count = counter.incrementAndGet();
LOGGER.info("Schedule timerTask {} times", count);
}
};
Timer timer = new Timer();
timer.schedule(timerTask, 1000L, 10 * 1000L;
}
}
上面的代碼定義了一個(gè) TimerTask
,在 TimerTask
中累加 執(zhí)行次數(shù)金麸,并通過 slf4j
進(jìn)行打印 (自帶執(zhí)行時(shí)間)擎析。然后通過 Timer
調(diào)度工具類調(diào)度 TimerTask
任務(wù),設(shè)置 初始化延遲時(shí)間 為 1s
挥下,定時(shí)執(zhí)行間隔 為 10s
揍魂,測試代碼如下:
public static void main(String[] args) {
TimerService timerService = new TimerService();
timerService.schedule();
}
觀察測試結(jié)果挪鹏,能夠發(fā)現(xiàn) TimerTask
配置的任務(wù)每隔 10s
被執(zhí)行了一次,執(zhí)行線程默認(rèn)都是 Timer-0
這個(gè)線程愉烙。
17:48:18.731 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 1 times
17:48:28.730 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 2 times
17:48:38.736 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 3 times
17:48:48.738 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 4 times
17:48:58.743 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 5 times
配置ScheduledExecutorService任務(wù)
ScheduledExecutorService
是 延時(shí)執(zhí)行 的線程池讨盒,對于 多線程 環(huán)境下的 定時(shí)任務(wù),推薦用 ScheduledExecutorService
代替 Timer
定時(shí)器步责。
創(chuàng)建一個(gè)線程數(shù)量為 4
的 任務(wù)線程池返顺,同一時(shí)刻并向它提交 4
個(gè)定時(shí)任務(wù),用于測試延時(shí)任務(wù)的 并發(fā)處理蔓肯。執(zhí)行 ScheduledExecutorService
的 scheduleWithFixedDelay()
方法遂鹊,設(shè)置任務(wù)線程池的 初始任務(wù)延遲時(shí)間 為 2
秒,并在上一次 執(zhí)行完畢時(shí)間點(diǎn) 之后 10
秒再執(zhí)行下一次任務(wù)蔗包。
public void scheduleWithFixedDelay() {
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
for (int i = 0; i < 4; i++) {
scheduledExecutor.scheduleWithFixedDelay(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10 * 1000L);
} catch (InterruptedException e) {
LOGGER.error("Interrupted exception", e);
}
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with fixed delay", count);
}, 2000L, 10 * 1000L, TimeUnit.MILLISECONDS);
}
LOGGER.info("Start to schedule");
}
測試結(jié)果如下秉扑,我們可以發(fā)現(xiàn)每隔 20
秒的時(shí)間間隔,就會(huì)有 4
個(gè)定時(shí)任務(wù)同時(shí)執(zhí)行调限。因?yàn)樵谌蝿?wù)線程池初始化時(shí)舟陆,我們同時(shí)向線程池提交了 4
個(gè)任務(wù),這 四個(gè)任務(wù) 會(huì)完全利用線程池中的 4
個(gè)線程進(jìn)行任務(wù)執(zhí)行耻矮。
20
秒是怎么來的秦躯?首先每個(gè)任務(wù)的 時(shí)間間隔 設(shè)置為 10
秒。其次因?yàn)椴捎玫氖?withFixedDelay
策略裆装,即當(dāng)前任務(wù)執(zhí)行的 結(jié)束時(shí)間踱承,作為下次延時(shí)任務(wù)的 開始計(jì)時(shí)節(jié)點(diǎn),并且每個(gè)任務(wù)在執(zhí)行過程中睡眠了 10
秒的時(shí)間哨免,累計(jì)起來就是 20
秒的時(shí)間茎活。
19:42:02.444 [main] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Start to schedule
19:42:14.449 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 3 times with fixed delay
19:42:14.449 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 1 times with fixed delay
19:42:14.449 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 2 times with fixed delay
19:42:14.449 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 4 times with fixed delay
19:42:34.458 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 7 times with fixed delay
19:42:34.458 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 5 times with fixed delay
19:42:34.458 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 8 times with fixed delay
19:42:34.458 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 6 times with fixed delay
創(chuàng)建一個(gè)線程數(shù)量為 4
的 任務(wù)線程池,同一時(shí)刻并向它提交 4
個(gè)定時(shí)任務(wù)琢唾,用于測試延時(shí)任務(wù)的 并發(fā)處理载荔。每個(gè)任務(wù)分別執(zhí)行 ScheduledExecutorService
的 scheduleAtFixedRate()
方法,設(shè)置任務(wù)線程池的 初始任務(wù)延遲時(shí)間 為 2
秒慧耍,并在上一次 開始執(zhí)行時(shí)間點(diǎn) 之后 10
秒再執(zhí)行下一次任務(wù)身辨。
public void scheduleAtFixedRate() {
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
for (int i = 0; i < 4; i++) {
scheduledExecutor.scheduleAtFixedRate(() -> {
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times at fixed rate", count);
}, 2000L, 10 * 1000L, TimeUnit.MILLISECONDS);
}
LOGGER.info("Start to schedule");
}
測試結(jié)果如下,我們可以發(fā)現(xiàn)每隔 10
秒的時(shí)間間隔芍碧,就會(huì)有 4
個(gè)定時(shí)任務(wù)同時(shí)執(zhí)行煌珊,因?yàn)樵谌蝿?wù)線程池初始化時(shí),我們同時(shí)向線程池提交了 4
個(gè)任務(wù)泌豆,這 四個(gè)任務(wù) 會(huì)完全利用線程池中的 4
個(gè)線程進(jìn)行任務(wù)執(zhí)行定庵。
19:31:46.837 [main] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Start to schedule
19:31:48.840 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 1 times at fixed rate
19:31:48.840 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 3 times at fixed rate
19:31:48.840 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 2 times at fixed rate
19:31:48.840 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 4 times at fixed rate
19:31:58.839 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 6 times at fixed rate
19:31:58.840 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 8 times at fixed rate
19:31:58.839 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 7 times at fixed rate
19:31:58.839 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 5 times at fixed rate
配置Spring Task任務(wù)
Spring
提供了 @Scheduled
注解來實(shí)現(xiàn) 定時(shí)任務(wù),@Scheduled
參數(shù)可以接受 兩種 定時(shí)的設(shè)置,一種是我們常用的 格林時(shí)間表達(dá)式 cron = "*/10 * * * * *"
蔬浙,另一種是 fixedRate = 10 * 1000L
猪落,兩種都表示每隔 10
秒執(zhí)行一次目標(biāo)任務(wù)。
參數(shù)說明:
- @Scheduled(fixedRate = 10 * 1000L):上一次 開始執(zhí)行時(shí)間點(diǎn) 之后
10
秒再執(zhí)行畴博。
@Scheduled(fixedRate = 10 * 1000L)
public void scheduleAtFixedRate() throws Exception {
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times at fixed rate", count);
}
- @Scheduled(fixedDelay = 10 * 1000L):上一次 執(zhí)行完畢時(shí)間點(diǎn) 之后
10
秒再執(zhí)行笨忌。
@Scheduled(fixedDelay = 10 * 1000L)
public void scheduleWithFixedDelay() throws Exception {
try {
TimeUnit.MILLISECONDS.sleep(10 * 1000L);
} catch (InterruptedException e) {
LOGGER.error("Interrupted exception", e);
}
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with fixed delay", count);
}
- @Scheduled(initialDelay = 2000L, fixedRate = 10 * 1000L):第一次延遲
2
秒后執(zhí)行,之后按fixedRate
的規(guī)則每10
秒執(zhí)行一次俱病。
@Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)
public void scheduleWithinitialDelayAndFixedDelay() throws Exception {
try {
TimeUnit.MILLISECONDS.sleep(10 * 1000L);
} catch (InterruptedException e) {
LOGGER.error("Interrupted exception", e);
}
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with fixed delay", count);
}
- @Scheduled(cron = "0/10 * * * * *"):根據(jù)
cron
表達(dá)式定義官疲,每隔10
秒執(zhí)行一次。
@Scheduled(cron = "0/10 * * * * *")
public void scheduleWithCronExpression() throws Exception {
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with ", count);
}
完整的代碼如下:
SpringTaskService.java
@Component
public class SpringTaskService {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringTaskService.class);
private AtomicLong counter = new AtomicLong();
@Scheduled(fixedDelay = 10 * 1000L)
public void scheduleWithFixedDelay() throws Exception {
try {
TimeUnit.MILLISECONDS.sleep(10 * 1000L);
} catch (InterruptedException e) {
LOGGER.error("Interrupted exception", e);
}
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with fixed delay", count);
}
@Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)
public void scheduleWithinitialDelayAndFixedDelay() throws Exception {
try {
TimeUnit.MILLISECONDS.sleep(10 * 1000L);
} catch (InterruptedException e) {
LOGGER.error("Interrupted exception", e);
}
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with fixed delay", count);
}
@Scheduled(fixedRate = 10 * 1000L)
public void scheduleAtFixedRate() throws Exception {
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times at fixed rate", count);
}
@Scheduled(cron = "0/10 * * * * *")
public void scheduleWithCronExpression() throws Exception {
long count = counter.incrementAndGet();
LOGGER.info("Schedule executor {} times with ", count);
}
}
查看日志亮隙,任務(wù)每 20
秒的時(shí)間間隔執(zhí)行一次途凫。每次定時(shí)任務(wù)在上次 執(zhí)行完畢時(shí)間點(diǎn) 之后 10
秒再執(zhí)行,在任務(wù)中設(shè)置 睡眠時(shí)間 為 10
秒溢吻。這里只驗(yàn)證了 @Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)维费。
2018-06-25 18:00:53.051 INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService : Schedule executor 1 times with fixed delay
2018-06-25 18:01:13.056 INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService : Schedule executor 2 times with fixed delay
2018-06-25 18:01:33.061 INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService : Schedule executor 3 times with fixed delay
2018-06-25 18:01:53.071 INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService : Schedule executor 4 times with fixed delay
2018-06-25 18:02:13.079 INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService : Schedule executor 5 times with fixed delay
配置任務(wù)線程池
上述配置都是基于 單線程 的任務(wù)調(diào)度,如何引入 多線程 提高 延時(shí)任務(wù) 的 并發(fā)處理 能力促王?
Spring Boot
提供了一個(gè) SchedulingConfigurer
配置接口犀盟。我們通過 ScheduleConfig
配置文件實(shí)現(xiàn) ScheduleConfiguration
接口,并重寫 configureTasks()
方法硼砰,向 ScheduledTaskRegistrar
注冊一個(gè) ThreadPoolTaskScheduler
任務(wù)線程對象即可且蓬。
@Configuration
public class ScheduleConfiguration implements SchedulingConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleConfiguration.class);
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(4);
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
taskScheduler.setThreadNamePrefix("schedule");
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setErrorHandler(t -> LOGGER.error("Error occurs", t));
return taskScheduler;
}
}
啟動(dòng) Spring Boot
引用欣硼,上面 SpringTaskService
配置的 4
個(gè)定時(shí)任務(wù)會(huì)同時(shí)生效题翰。
2018-06-20 20:37:50.746 INFO 8142 --- [ schedule1] i.o.s.sample.spring.SpringTaskService : Schedule executor 1 times at fixed rate
2018-06-20 20:38:00.001 INFO 8142 --- [ schedule3] i.o.s.sample.spring.SpringTaskService : Schedule executor 2 times with
2018-06-20 20:38:00.751 INFO 8142 --- [ schedule1] i.o.s.sample.spring.SpringTaskService : Schedule executor 3 times at fixed rate
2018-06-20 20:38:02.748 INFO 8142 --- [ schedule2] i.o.s.sample.spring.SpringTaskService : Schedule executor 4 times with fixed delay
2018-06-20 20:38:10.005 INFO 8142 --- [ schedule4] i.o.s.sample.spring.SpringTaskService : Schedule executor 5 times with
2018-06-20 20:38:10.747 INFO 8142 --- [ schedule3] i.o.s.sample.spring.SpringTaskService : Schedule executor 6 times at fixed rate
2018-06-20 20:38:20.002 INFO 8142 --- [ schedule2] i.o.s.sample.spring.SpringTaskService : Schedule executor 7 times with
2018-06-20 20:38:20.747 INFO 8142 --- [ schedule4] i.o.s.sample.spring.SpringTaskService : Schedule executor 8 times at fixed rate
觀察日志,線程名前綴 為 schedule
诈胜,可以發(fā)現(xiàn) Spring Task
將 @Scheduled
注解配置的 4
個(gè)任務(wù)豹障,分發(fā)給我們配置的 ThreadPoolTaskScheduler
中的 4
個(gè)線程并發(fā)執(zhí)行。
小結(jié)
本文介紹了基于單節(jié)點(diǎn)的定時(shí)任務(wù)調(diào)度及實(shí)現(xiàn)焦匈,包括 JDK
原生的 Timer
和 ScheduledExecutorService
血公,以及 Spring 3.0
以后自帶的基于注解的 Spring Task
任務(wù)調(diào)度方式。除此之外缓熟,重點(diǎn)闡述了基于 固定延時(shí)累魔、固定頻率 和 cron
表達(dá)式 的不同之處,并對 ScheduledExecutorService
和 Spring Scheduler
的 線程池并發(fā)處理 進(jìn)行了測試够滑。
歡迎關(guān)注技術(shù)公眾號: 零壹技術(shù)棧
本帳號將持續(xù)分享后端技術(shù)干貨垦写,包括虛擬機(jī)基礎(chǔ),多線程編程彰触,高性能框架梯投,異步、緩存和消息中間件,分布式和微服務(wù)分蓖,架構(gòu)學(xué)習(xí)和進(jìn)階等學(xué)習(xí)資料和文章尔艇。