定时器的使用场景分析
面试官: 我看你已经有一定工作年限了,那请问有没有研究得比较深的一些技术栈呢?
你: 目前没有太深的研究,暂时还主要是处于会使用的阶段,我用过xxx,能干活肯定是没问题的!
面试官: 那定时器在项目中有用过吗,平时都是怎么使用的?
你: 当然有,我们是基于SpringBoot做的定时器,使用很简单,就是打上注解就行.
面试官: 既然你有使用过,那我们就聊一下SpringBoot定时器使用层面的问题,不问深的,甚至分布式集群也暂时不考虑了,就只考虑最简单的单线程模型,那请问假如我定时任务是5秒执行一次,但是第一个任务就执行了8秒,第二个会怎么样呢?你详细说一下各种情况.
你: 这个...这个真没有考虑过
面试官: 我都还没问原理,我这只是问最最基本的使用问题,要不今天先到这里,你回去等通知吧.
定时器的使用分析
SpingBoot的定时器我相信大家都会使用,即使不会使用的,随便搜索一下,相信都能在30分钟内上手.但是其实Spring的定时器有三种模式,分别是fixedDelay
、cron
、fixedRate
.那这三种分别有什么区别呢?我们来分别写一个SpringBoot的定时器Demo来体验一下.
fixedDelay方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @Component public class ScheduleHandle { private final Logger log = LoggerFactory.getLogger(ScheduleHandle. class ); private List<Integer> index = Arrays.asList( 8 * 1000 , 3 * 1000 , 6 * 1000 , 2 * 1000 , 2 * 1000 ); private AtomicInteger atomicInteger = new AtomicInteger( 0 ); @Scheduled (fixedDelay = 3 * 1000 ) public void fixedDelay() throws Exception { int i = atomicInteger.get(); if (i < 5 ) { Integer sleepTime = index.get(i); log.info( "第{}个任务开始执行,执行时间为{}ms" , i, sleepTime); Thread.sleep(sleepTime); atomicInteger.getAndIncrement(); } } } |
该方式最简单,在上一个任务执行完成之后,间隔3秒(因为@Scheduled(fixedDelay = 3 * 1000)
)后,执行下一个任务.这种是最容易理解的,所以放在第一个来讲.用一个图来表示的话,更容易理解.如下:
可以用输出日志验证上述结论
1 2 3 4 5 | 2019 - 04 - 07 21 : 59 : 11.761 INFO 29372 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 0 个任务开始执行,执行时间为8000ms 2019 - 04 - 07 21 : 59 : 22.772 INFO 29372 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 1 个任务开始执行,执行时间为3000ms 2019 - 04 - 07 21 : 59 : 28.777 INFO 29372 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 2 个任务开始执行,执行时间为6000ms 2019 - 04 - 07 21 : 59 : 37.783 INFO 29372 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 3 个任务开始执行,执行时间为2000ms 2019 - 04 - 07 21 : 59 : 42.785 INFO 29372 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 4 个任务开始执行,执行时间为2000ms |
corn
1 2 3 4 5 6 7 8 9 10 | @Scheduled (cron = "0/5 * * * * ? " ) public void cron() throws Exception { int i = atomicInteger.get(); if (i < 5 ) { Integer sleepTime = index.get(i); log.info( "第{}个任务开始执行,执行时间为{}ms" , i, sleepTime); Thread.sleep(sleepTime); atomicInteger.getAndIncrement(); } } |
因为上方代码配置如下:
1 2 | //每5s执行一次 @Scheduled (cron = "0/5 * * * * ? " ) |
因此你可以理解为5s就是一个周期.这就相当于在宿舍洗澡,因为只有一个洗澡位置(单线程),所以每次只能进去一个人,然后舍长在门口,每5s看一下有没有空位,有空位的话叫下一个进去洗澡.
- 第5秒的时候,舍长看了一下,发现第一个同学还没有出来.
- 第二个周期,也就是第10秒的时候再看一下.发现已经有空位了,那么就叫第二个同学进去洗.
- 第三个周期,也就是15秒的时候,又瞄了一眼,发现有空位了,叫第三个同学进去洗.
- 第四个周期,也就是20秒的时候,发现没有空位.
- 第五个周期的时候,也就是25秒的时候.发现有空位了,接着叫下一个进去洗.剩下的不再分析.
用一张图来表示如下:
可以用输出日志验证上述结论
1 2 3 4 5 | 2019 - 04 - 07 22 : 15 : 30.002 INFO 29385 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 0 个任务开始执行,执行时间为8000ms 2019 - 04 - 07 22 : 15 : 40.001 INFO 29385 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 1 个任务开始执行,执行时间为3000ms 2019 - 04 - 07 22 : 15 : 45.001 INFO 29385 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 2 个任务开始执行,执行时间为6000ms 2019 - 04 - 07 22 : 15 : 55.001 INFO 29385 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 3 个任务开始执行,执行时间为2000ms 2019 - 04 - 07 22 : 16 : 00.001 INFO 29385 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 4 个任务开始执行,执行时间为2000ms |
fixedRate
1 2 3 4 5 6 7 8 9 10 | @Scheduled (fixedRate = 5 * 1000 ) public void fixedRate() throws Exception { int i = atomicInteger.get(); if (i < 5 ) { Integer sleepTime = index.get(i); log.info( "第{}个任务开始执行,执行时间为{}ms" , i, sleepTime); Thread.sleep(sleepTime); atomicInteger.getAndIncrement(); } } |
还是要拿洗澡的这个例子来说.但是该方式和cron
的方式有很大不同
因上方代码配置如下:
1 | @Scheduled (fixedRate = 5 * 1000 ) |
你可以理解为舍长预算每个同学洗澡的时间是5秒,但是第一个同学进去洗澡,用了8秒.
- 第二个同学本来应该在第5秒的时候就进去的,结果第一个同学出来的时候,已经是第8秒了,那么舍长就赶紧催第二个同学进去,把时间进度追回来.
- 第二个同学知道时间紧,洗了3秒就出来.此时舍长发现,第三个同学,原本应该是在第10秒进去的,结果现在已经到了第11秒(8+3),那么就赶紧催第三个同学进去.
- 第三个同学沉醉其中,难以自拔的洗了6秒.出来的时候已经是第17秒(8+3+6).舍长掐指一算,发现第四个同学原本应该是第15秒的时候就进去了.结果现在都17秒了,时间不等人,催促第四个同学进去赶紧洗.
- 第四个同学只洗了2秒就出来了,出来的时候,舍长看了一下时间,是第19秒."有原则"的舍长发现,第5个同学原本预算是20秒的时候进去的,结果现在才19秒,不行,那让他在外面玩1秒的手机,等20秒的时候再进去.
用一张图来表示如下:
可以用输出日志验证上述结论
1 2 3 4 5 | 2019 - 04 - 07 22 : 18 : 44.814 INFO 29390 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 0 个任务开始执行,执行时间为8000ms 2019 - 04 - 07 22 : 18 : 52.819 INFO 29390 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 1 个任务开始执行,执行时间为3000ms 2019 - 04 - 07 22 : 18 : 55.824 INFO 29390 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 2 个任务开始执行,执行时间为6000ms 2019 - 04 - 07 22 : 19 : 01.829 INFO 29390 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 3 个任务开始执行,执行时间为2000ms 2019 - 04 - 07 22 : 19 : 04.816 INFO 29390 --- [pool- 1 -thread- 1 ] com.toby.demo.job.ScheduleHandle : 第 4 个任务开始执行,执行时间为2000ms |
本博客文章均已测试验证,欢迎评论、交流、点赞。
部分文章来源于网络,如有侵权请联系删除。
转载请注明原文链接:https://www.cnblogs.com/sueyyyy/p/15071528.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix