springtask 任务执行暂停问题

项目场景:

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

单线程阻塞问题解决,另外一种利用线程池的解决方式可以看参考博文

参考博文:springTask阻塞问题_QAQiiii的博客-CSDN博客

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值