前面一篇博文 《Spring之定时任务基本使用篇》 介绍了Spring环境下,定时任务的简单使用姿式,也留了一些问题,这一篇则但愿能针对这些问题给个答案java
前面一篇博文,抛出了下面的几个问题,接下来则围绕问题进行分析linux
若是默认是串行的git
若是是并发执行的github
如何确认一个项目中的多个定时任务是串行执行仍是并发执行呢?要想验证这个功能,最好的法子就是写个testcase,好比定义两个定时任务,在其中一个任务中写个死循环,看另一个任务是否会正常执行安全
@Scheduled(cron = "0/1 * * * * ?") public void sc1() throws InterruptedException { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); while (true) { Thread.sleep(5000); } } @Scheduled(cron = "0/1 * * * * ?") public void sc2() { System.out.println(Thread.currentThread().getName() + " | sc2 " + System.currentTimeMillis()); }
首先咱们分析的是 sc1和sc2这两个任务的执行是串行仍是并行的,暂时先不考虑 sc1 调用时阻塞,下一秒是不是开新的线程再调用sc1并发
实际运行,GIF图演示以下异步
上图的结果,印证了默认的状况下,多个定时任务时串行执行的;若是一个任务出现阻塞,其余的任务都会受到影响async
既然是顺序执行的,那么优先级怎么定?每次都是固定的,仍是随机的呢?源码分析
要验证上面的方法,也容易,一样两个任务,看他们的输出是否会乱掉,若是每次都是任务1打印完再打印任务2,那就是固定优先级的;不然每次调度时,顺序很差说学习
测试代码以下
@Scheduled(cron = "0/1 * * * * ?") public void sc1() { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); } @Scheduled(cron = "0/1 * * * * ?") public void sc2() { System.out.println(Thread.currentThread().getName() + " | sc2 " + System.currentTimeMillis()); }
实测结果以下
从输出得出结论:顺序是串掉的,并无表现出明显的优先级关系
接下来的问题就是我但愿这些任务能够并发执行,能够实现么?
固然是能够,用起来也比较简单,首先是在Application上添加注解@EnableAsync
,开启异步调用,而后再计划任务上加上@Async
注解便可,一个简单的demo以下
@EnableAsync @EnableScheduling @SpringBootApplication public class QuickMediaApplication { public static void main(String[] args) { SpringApplication.run(QuickMediaApplication.class, args); } @Scheduled(cron = "0/1 * * * * ?") @Async public void sc1() { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); } }
上面执行以后,查看输出(异步调度时,理论上线程名应该不同)
从上面的输出,能够简单的推理,每次调度上面的任务都是新开了一个线程来作的,因此若是在定时任务中写了死循环,是否会致使无限线程,最后整个进程崩掉?
额外提一句,linux系统下单进程的线程数是有上线的,查看命令为:
ulimit -u
在测试以前,先看下上面的正常任务执行,以下面的动图,线程数并无夸张的长法
接下来换成死循环的调度方式,实际测试以下,线程数蹭蹭的上涨
因此使用默认的异步调用方式,并非一个好注意,说不许就被玩死了本身都不知道,那么能够用本身的线程池来管理这些异步任务么?
用自定义的线程池来取代默认线程管理方式,无疑是一个更加安全和灵活的方式,使用起来也并不麻烦,和日常建立线程池的套路没什么区别,要在Spring生态中使用,就把它搞成bean便可
直接借助Spring的线程池ThreadPoolTaskExecutor
@Bean public AsyncTaskExecutor asyncTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("yhh-schedule-"); executor.setMaxPoolSize(10); executor.setCorePoolSize(3); executor.setQueueCapacity(0); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); return executor; } @Scheduled(cron = "0/1 * * * * ?") @Async public void sc1() throws InterruptedException { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); while (true) { Thread.sleep(1000 * 5); } }
实际演示的结果以下,最多10个线程,再提交的任务直接丢弃
简单说一下,用自定义线程池的好处:
原本这篇博文在昨天即8月2号就应该写完的,结果晚上生产环境下除了点问题,解决线上故障以后就比较晚了,留到了今天,哎,拖延症也是要不得。。。
下面小结Spring中定时任务的几个知识点
@Async
注解可使定时任务异步调度;可是须要开启配置,在启动类上添加 @EnableAsync
注解一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,已上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
小灰灰Blog&公众号
知识星球