赞
踩
SpringBoot项目,多个定时任务执行,注解为@Scheduled(七子表达式)
项目启动,正常执行一段时间,其中一个定时任务停止运行,查看日志报错信息出现Scheduled字样,确定是定时任务出错
1.由于SpringBoot执行多个定时任务为单线程执行,即执行完A任务才执行B任务,则当A任务与B任务的周期时间不一致,且其中一个任务阻塞时,另一个任务会同步阻塞
2.项目中其中一个定时任务为http请求,且在定时任务停止运行前,该http请求有报错。怀疑http请求过程中出现某种错误导致http请求僵死,致使线程不再往下执行,最终导致后续的定时任务也不再执行
- @Component
- public class Test {
-
- private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
-
- private int count = 1;
-
- @Scheduled(cron = "0/3 * * * * ?")
- public void first(){
- System.out.println("A:现在时间:" + format.format(new Date()) + "---" + Thread.currentThread().getName());
- }
-
- @Scheduled(cron = "0/5 * * * * ?")
- public void second(){
- if(count == 5){
- try {
- Thread.sleep(15000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- throw new RuntimeException();
- }
- System.out.println("B:现在时间:" + format.format(new Date()) + "---" + Thread.currentThread().getName());
- count++;
- }
- }
- A:现在时间:15:04:09---scheduling-1
- B:现在时间:15:04:10---scheduling-1
- A:现在时间:15:04:12---scheduling-1
- A:现在时间:15:04:15---scheduling-1
- B:现在时间:15:04:15---scheduling-1
- A:现在时间:15:04:18---scheduling-1
- B:现在时间:15:04:20---scheduling-1
- A:现在时间:15:04:21---scheduling-1
- A:现在时间:15:04:24---scheduling-1
- java.lang.RuntimeException: null
- at com.fl.video.stat.test.Test.second(Test.java:35) ~[classes/:na]
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
- at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
- at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_161]
- at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_161]
- at java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_161]
- at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_161]
- at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_161]
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_161]
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_161]
- at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]
- A:现在时间:15:04:40---scheduling-1
- A:现在时间:15:04:42---scheduling-1
由上述代码及日志可知:
B应该在15:04:25再次执行,A应该在15:04:27再次执行,但由于代码中的sleep以及报错,导致B被阻塞了,此时A也被阻塞了,也就印证了猜测的第一条。但在15秒以后即15:04:40,A再次恢复了运行,并没有因为B的报错而导致A的停止运行
针对于第一条猜测:
单个线程定时任务防止阻塞
1.启动类加入注解支持@EnableAsync
2.在定时任务方法上加入注解@Async这里其实就是通过增加注解来使SpringBoot单线程定时任务变成了多线程
还有另外一种方法为自己定义线程池,同样可以解决这个问题
- @Component
- public class Test {
-
- private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
-
- private int count = 1;
-
- @Scheduled(cron = "0/3 * * * * ?")
- @Async
- public void first(){
- System.out.println("A:现在时间:" + format.format(new Date()) + "---" + Thread.currentThread().getName());
- }
-
- @Scheduled(cron = "0/5 * * * * ?")
- @Async
- public void second(){
- if(count == 5){
- try {
- Thread.sleep(15000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- throw new RuntimeException();
- }
- System.out.println("B:现在时间:" + format.format(new Date()) + "---" + Thread.currentThread().getName());
- count++;
- }
- }
- A:现在时间:15:24:04---task-1
- B:现在时间:15:24:05---task-3
- A:现在时间:15:24:06---task-4
- A:现在时间:15:24:09---task-5
- B:现在时间:15:24:10---task-6
- A:现在时间:15:24:12---task-7
- A:现在时间:15:24:15---task-2
- B:现在时间:15:24:15---task-8
- A:现在时间:15:24:18---task-1
- A:现在时间:15:24:21---task-4
- A:现在时间:15:24:24---task-5
- A:现在时间:15:24:27---task-7
- A:现在时间:15:24:30---task-2
- A:现在时间:15:24:33---task-1
- java.lang.RuntimeException: null
- at com.fl.video.stat.test.Test.second(Test.java:38) ~[classes/:na]
- at com.fl.video.stat.test.Test$$FastClassBySpringCGLIB$$534a3a02.invoke(<generated>) ~[classes/:na]
- at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
- at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) ~[na:1.8.0_161]
- at java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:1.8.0_161]
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_161]
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_161]
- at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_161]
- A:现在时间:15:24:36---task-5
单线程阻塞问题解决,另外一种利用线程池的解决方式可以看参考博文
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。