原文:https://www.cnblogs.com/domi22/p/9418433.html
1、spring boot使用注解實(shí)現(xiàn)定時(shí)器
@EnableScheduling
@Component
public class TestTimer {
@Scheduled(cron="*/1 * * * * ?")
public void testOne() {
System.out.println("第一個(gè)"+"每1秒執(zhí)行一次"+Thread.currentThread().getName());
}
@Scheduled(fixedRate=1000)
public void testTwo() {
System.out.println("第二個(gè)"+"每1秒執(zhí)行一次"+Thread.currentThread().getName());
}
}
這樣項(xiàng)目啟動(dòng)的時(shí)候定時(shí)器就啟動(dòng)了控淡,但是通過(guò)打印線程名可以發(fā)現(xiàn)嫌吠,這兩個(gè)任務(wù)是同一個(gè)線程。
如果將第二個(gè)任務(wù)睡眠10秒掺炭,模擬延時(shí)辫诅。
@EnableScheduling
@Component
public class TestTimer {
@Scheduled(cron="*/1 * * * * ?")
public void testOne() {
System.out.println("第一個(gè)"+"每1秒執(zhí)行一次"+Thread.currentThread().getName());
}
@Scheduled(fixedRate=1000)
public void testTwo() {
System.out.println("第二個(gè)"+"每1秒執(zhí)行一次"+Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
會(huì)發(fā)現(xiàn)不但自己的任務(wù)丟失了,也造成了其他任務(wù)的丟失涧狮。
問(wèn)題1:?jiǎn)尉€程任務(wù)丟失
解決辦法:采用異步的方式執(zhí)行調(diào)度任務(wù)炕矮,配置 Spring 的 @EnableAsync么夫,在執(zhí)行定時(shí)任務(wù)的方法上標(biāo)注 @Async配置任務(wù)執(zhí)行池
@EnableScheduling
@EnableAsync
@Component
public class TestTimer {
@Async
@Scheduled(cron="*/1 * * * * ?")
public void testOne() {
System.out.println("第一個(gè)"+"每1秒執(zhí)行一次"+Thread.currentThread().getName());
}
@Async
@Scheduled(fixedRate=1000)
public void testTwo() {
System.out.println("第二個(gè)"+"每1秒執(zhí)行一次"+Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
注意:@Async所使用的線程池容量為100
3.配置線程池大小
雖然上面的方式已經(jīng)解決了我們的問(wèn)題,但是總覺(jué)得不太好肤视,有時(shí)候我們需要異步執(zhí)行任務(wù)魏割,但是又不需要這么多的線程的時(shí)候,我們可以使用下面的配置來(lái)設(shè)置線程池的大小
配置文件:
@Configuration
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor1() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("taskExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
return executor;
}
}
問(wèn)題2:在分布式系統(tǒng)下钢颂,重復(fù)執(zhí)行的問(wèn)題钞它。
解決方式1:可以使用redis的分布式鎖保證spring schedule集群只執(zhí)行一次。 redis分布式鎖是通過(guò)setnx命令實(shí)現(xiàn)的殊鞭。該命令的作用是遭垛,當(dāng)往redis中存入一個(gè)值時(shí),會(huì)先判斷該值對(duì)應(yīng)的key是否存在操灿,如果存在則返回0锯仪,如果不存在,則將該值存入redis并返回1趾盐。(但是在分布式跨時(shí)區(qū)部署的時(shí)候庶喜,依然無(wú)法避免重復(fù)執(zhí)行)
解決方式2:可以通過(guò)使用shedlock將spring schedule上鎖。詳細(xì)見(jiàn):https://segmentfault.com/a/1190000011975027
問(wèn)題3:如果使用異步多線程方式救鲤,會(huì)有并發(fā)安全問(wèn)題久窟。
如果想有的定時(shí)任務(wù)不考慮并發(fā)安全使用@Async注解,有的定時(shí)任務(wù)需要考慮線程并發(fā)使用同步阻塞方式本缠。
@EnableScheduling
@EnableAsync
@Component
public class TestTimer {
@Scheduled(cron="*/1 * * * * ?")
public void testOne() throws InterruptedException {
System.out.println("任務(wù)1====>"+Thread.currentThread().getName());
Thread.sleep(10000); //模擬延時(shí)
}
@Async
@Scheduled(cron="*/1 * * * * ?")
public void testTwo() throws InterruptedException {
System.out.println("任務(wù)2====>"+Thread.currentThread().getName());
}
}
發(fā)現(xiàn)任務(wù)2還是會(huì)被任務(wù)1阻塞住斥扛,好像沒(méi)什么好辦法?
待解決:?jiǎn)栴}3