zoukankan      html  css  js  c++  java
  • Spring @Scheduled @Async联合实现调度任务(2017.11.28更新)

    定时任务之前一直用的是quartz之类,但是注意到Spring中其实也提供了一种简单的调度注释@Scheduled,也就想尝一下鲜..

    代码示意如下:

    @Component
    @EnableScheduling
    public class AsyncTaskHandlerTask {
    
        @Scheduled(fixedDelay = 1000)
        public void task1() {
             //输出日志
        }
    
        @Scheduled(fixedDelay = 1000)
        public void task2() {
             //输出日志    
        }
    }

    执行了一下,完全ok,日志打印正常,2个任务也都正常定时执行了.那好,添加些业务逻辑进去:

    @Component
    @EnableScheduling
    public class AsyncTaskHandlerTask {
    
        @Scheduled(fixedDelay = 1000)
        public void task1() {
             while(true){
                ....
             }
        }
    
        @Scheduled(fixedDelay = 1000)
        public void task2() {
             while(true){
                ....
             }
         }
    
    }

    再启动,咦,奇怪了,怎么定时任务没有执行呢?倘使我之前没有输出日志试验,我可能就认为注解的用法错了呢...重新添加日志,下断点重跟了一下启动过程发现:

    程序进入到while死循环后就卡死了,没有再继续启动另一个定时任务了.通过现象可知@Scheduled启动过程是一个单线程同步启动过程,故一旦中途被阻塞,会导致整个启动过程阻塞,

    其余的定时任务都不会启动.这明显很奇怪,网上的教程大多数是xml配置形式,Spring的官网我这头打开又奇慢无比..但是从xml的配置形式可知需要配置一个线程池来启动定时任

    务.但是Javaconfig形式的则没有说明.但是我查询到了另一个注解@Async,这个异步注解我是使用过的,可以指定线程池,打到方法上后便会以指定的线程池来执行方法.然后解决方案来了:

    @Component
    @EnableScheduling
    public class AsyncTaskHandlerTask {
    
        @Scheduled(fixedDelay = 1000)
        @Async
        public void task1() {
             while(true){
                ....
             }
        }
    
        @Scheduled(fixedDelay = 1000)
        @Async
        public void task2() {
             while(true){
                ....
             }
         }
    
    }

    再次启动,不会再被阻塞.

    2017-11-28更新:

    本来以为找到了正解,结果证明是误入了歧途..以上是完全错误的使用,虽然看起来貌似是正确的..正应了一句话啊,啥都不如看源码啊...

    太懒了,本来这个应该再单独开个文章再说的,但是太懒了...

    进入正题,按照如上实现部署后突然发现一个诡异的问题,定时任务发生了异常的"阻塞"现象,某个任务突然看起来不再执行了,直接卡死了.第一印象是死锁了,直接jstack生成了一份堆栈信息,仔细分析后发现一个诡异问题,定时任务并没有像Scheduled定义的那样一次结束后再执行下一次,而是并发执行多次,直接将Async定义的线程池跑满.那么问题也就好解释了,Async注解后,直接异步在新线程中执行任务,由于异步执行Scheduled认为上一次已经执行完马上开始执行下一次,导致不停执行定时任务直接跑满Async使用的线程池.那么这实际上是一种错误的使用方法..怎么办?

    只好点了@EnableScheduling注解看一下源码中怎么说的吧.

    * <p>When more control is desired, a {@code @Configuration} class may implement
     * {@link SchedulingConfigurer}. This allows access to the underlying
     * {@link ScheduledTaskRegistrar} instance. For example, the following example
     * demonstrates how to customize the {@link Executor} used to execute scheduled
     * tasks:

    实际上已经说的很明白了,更多的控制,只需要继承 SchedulingConfigurer 这个类,之前没有找到对应xml中配置线程池的方法也正是如此.

     * public class AppConfig implements SchedulingConfigurer {
     *
     *     @Override
     *     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
     *         taskRegistrar.setScheduler(taskExecutor());
     *     }
     *
     *     @Bean(destroyMethod="shutdown")
     *     public Executor taskExecutor() {
     *         return Executors.newScheduledThreadPool(100);
     *     }
     * }

    标红处就是使用线程池的配置,之后再执行不仅以多线程来启动定时任务,而且也不会出现定时任务重复并发执行的问题.至此此问题圆满解决.再感叹下啥都不如看源码.

  • 相关阅读:
    HTML课堂笔记
    pycrul使用
    计算机网络概述
    重温冒泡排序
    初识MySQL
    宝塔Linux面板安装教程
    运维和shell
    nginx学习总结
    docker学习汇总
    linux 安装redis 完整步骤
  • 原文地址:https://www.cnblogs.com/chyu/p/7801521.html
Copyright © 2011-2022 走看看