定时任务怎么样避免并发,意思是同一个任务当第一个执行完了,第二才执行,不是一到了间隔时间,不管第一个是否执行完成,第二个就开始执行。
下面小弟整理的笔记
@Scheduled注解是spring boot提供的用于定时任务控制的注解,主要用于控制任务在某个指定时间执行,或者每隔一段时间执行,注意需要配合@EnableScheduling使用,配置@Scheduled主要有三种配置执行时间的方式:cron,fixedRate,fixedDelay
cron
cron是@Scheduled的一个参数,是一个字符串,以空格隔开,允许6或7个域,分别表示秒,分,时,日,月,周,年
fixedRate
fixedRate表示自上一次执行时间之后多长时间执行,以ms为单位
fixedDelay
fixedDelay与fixedRate有点类似,不过fixedRate是上一次开始之后计时,fixedDelay是上一次结束之后计时,也就是说,fixedDelay表示上一次执行完毕之后多长时间执行,单位也是ms
initialDelay
initialDelay表示首次延迟多长时间后执行,单位ms,之后按照其他属性指定的规则执行,需要指定别的属性其中一个规则
下面是玩的过程 spring boot项目
正常玩
@Scheduled(cron = "0/10 * * * * ?")
public void cs1(){
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
@Scheduled(cron = "0/10 * * * * ?")
public void cs2(){
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
System.out.println("第二个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
输出结果
可以看出是单线程环境,但是不满足我要求,然后看到说可以加个注解实现异步
其中一个方法加上@Async启用异步
@Async
@Scheduled(cron = "0/10 * * * * ?")
public void cs1(){
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
@Scheduled(cron = "0/10 * * * * ?")
public void cs2(){
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
System.out.println("第二个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
输出结果
但是这样不行,可能我上个任务数据太多都没执行完成,这也就会导致数据重复消费了,不科学,继续找
网上找了一下,好多地方都推荐这两个属性fixedDelay 和 fixedRate,分别干啥上面写了
fixedRate走一波
@Async
@Scheduled(fixedRate = 1000*10)
public void cs1(){
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
@Scheduled(cron = "0/10 * * * * ?")
public void cs2(){
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
System.out.println("第二个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
输出结果
假装有图,结果同上,不管执行不,到点就执行
fixedDelay走一波
是否可以就看这个了,毕竟fixedDelay这个属性表示上一次执行完毕之后多长时间执行,看字面意思,结束了,才会执行下一次,心心念念的走一波
@Async
@Scheduled(fixedDelay = 1000*10)
public void cs1(){
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
@Scheduled(cron = "0/10 * * * * ?")
public void cs2(){
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
System.out.println("第二个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
见证奇迹的输出结果
假装有结果吧,我都不想看
结论
- spring 是单线程的,不管多少个任务都是一个线程再跑
- 某个方法使用注解 @Async,开启异步执行,那个其他定时任务依然遵循上个结论,使用了@Async这个注解的会多个线程执行,不管选择 cron,fixedRate,fixedDelay这三里的谁都一样
问题
- 只有一个任务单独执行,其他任务依然共用一个线程
- 单独执行的这个任务,需要操作数据库,所以只能一个时间只能有一个线程在执行
- 这个定时任务,每分钟执行一次,大多数时候没有数据操作
- 有数据的时候,一般执行三到五分钟
所以问题来了,每分钟执行一次,有数据的时候,此任务没有执行完成,单线程的时候,会被其他定时任务阻塞,针对这个定时任务,开启异步处理,那此任务就会在没有执行完成的时候,下个一分钟间隔到来前又执行一次
咋整!!咋整!!咋整!!!!!
上面已解决
代码
@Async
@Scheduled(cron = "0/10 * * * * ?")
public void cs1(){
if (i == 1){
i++;
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
i--;
}else {
System.out.println("定时任务未执行" + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
}
}
// @Async
@Scheduled(cron = "0/10 * * * * ?")
public void cs2(){
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
System.out.println("第二个定时任务结束 : " + LocalDateTime.now().toLocalTime() +
"\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
输出结果
现在有个小问题
到时间之后定时任务是另外一个线程在执行,但是我没有弄线程池,代码里面也并没有Thread.sleep(60*1000)
这种线程相关的代码,那么到时候新开的线程是怎么样的,对线程理解有点弱,如果按照这种写法会不会有问题,有问题是什么样的问题