Spring定时任务@scheduled多线程使用@Async注解示例

 更新时间:2023年11月24日 10:58:00   作者:怕翻船的忒修斯  
这篇文章主要为大家介绍了Spring定时任务@scheduled多线程使用@Async注解示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1.开篇

Spring定时任务@Scheduled注解使用方式浅窥这篇文章里面提及过,spring的定时任务默认是单线程的,他在某些场景下会造成堵塞,那么如果我们想让每一个任务都起一条线程去执行呢?

2.使用@Async

我们可以使用Spring的@Async注解十分容易的实现多线程的任务执行。
测试代码:

@Scheduled(cron = "0/2 * * * * ?")
    @Async
    public void doTask() throws InterruptedException {
        logger.info(Thread.currentThread().getName()+"===task run");
        Thread.sleep(6*1_000);
        logger.info(Thread.currentThread().getName()+"===task end");
    }

结果日志

2018-06-12 16:02:42.005 [taskExecutor-97] INFO  service.task.testTask -taskExecutor-97===task run
2018-06-12 16:02:42.007 [taskExecutor-94] INFO  service.task.testTask -taskExecutor-94===task end
2018-06-12 16:02:44.004 [taskExecutor-98] INFO  service.task.testTask -taskExecutor-98===task run
2018-06-12 16:02:44.015 [taskExecutor-95] INFO  service.task.testTask -taskExecutor-95===task end
2018-06-12 16:02:46.004 [taskExecutor-99] INFO  service.task.testTask -taskExecutor-99===task run
2018-06-12 16:02:46.014 [taskExecutor-96] INFO  service.task.testTask -taskExecutor-96===task end
2018-06-12 16:02:48.004 [taskExecutor-100] INFO  service.task.testTask -taskExecutor-100===task run
2018-06-12 16:02:48.010 [taskExecutor-97] INFO  service.task.testTask -taskExecutor-97===task end
2018-06-12 16:02:50.005 [taskExecutor-1] INFO  service.task.testTask -taskExecutor-1===task run
2018-06-12 16:02:50.008 [taskExecutor-98] INFO  service.task.testTask -taskExecutor-98===task end
2018-06-12 16:02:52.006 [taskExecutor-2] INFO  service.task.testTask -taskExecutor-2===task run

截取部分结果日志我们可以看到,在上一个任务6s的执行时间内,下一个任务并没有等待上一个任务结束,而是在任务开始时间直接开启了一条新的线程进行执行。
仔细观察结果我们还可以发现,每条结果都是一条新的线程,直到100时,才又从第一条线程开始。这是因为在默认不做配置的情况下,@Async所使用的线程池容量为100,每次需要的时候都会从中拿出一条,直到用完,才会等待之前的线程释放,不会再自己扩容。
下面我们稍稍改下代码来证实一下:

@Scheduled(cron = "0/2 * * * * ?")
    @Async
    public void doTask() throws InterruptedException {
        logger.info(Thread.currentThread().getName()+"===task run");
        Thread.sleep(300*1_000);
        logger.info(Thread.currentThread().getName()+"===task end");
    }

这次我让任务执行的时间等于300s,大于100条线程总间隔时间来耗尽线程池中的线程。

结果日志

2018-06-12 16:26:44.411 [taskExecutor-93] INFO  service.task.testTask -taskExecutor-93===task run
2018-06-12 16:26:46.853 [taskExecutor-94] INFO  service.task.testTask -taskExecutor-94===task run
2018-06-12 16:26:48.008 [taskExecutor-95] INFO  service.task.testTask -taskExecutor-95===task run
2018-06-12 16:26:50.008 [taskExecutor-96] INFO  service.task.testTask -taskExecutor-96===task run
2018-06-12 16:26:52.006 [taskExecutor-97] INFO  service.task.testTask -taskExecutor-97===task run
2018-06-12 16:26:54.008 [taskExecutor-98] INFO  service.task.testTask -taskExecutor-98===task run
2018-06-12 16:26:56.006 [taskExecutor-99] INFO  service.task.testTask -taskExecutor-99===task run
2018-06-12 16:26:58.005 [taskExecutor-100] INFO  service.task.testTask -taskExecutor-100===task run

2018-06-12 16:28:40.142 [taskExecutor-1] INFO  service.task.testTask -taskExecutor-1===task end
2018-06-12 16:28:40.149 [taskExecutor-1] INFO  service.task.testTask -taskExecutor-1===task run
2018-06-12 16:28:42.117 [taskExecutor-2] INFO  service.task.testTask -taskExecutor-2===task end
2018-06-12 16:28:42.121 [taskExecutor-2] INFO  service.task.testTask -taskExecutor-2===task run
2018-06-12 16:28:44.253 [taskExecutor-3] INFO  service.task.testTask -taskExecutor-3===task end
2018-06-12 16:28:44.257 [taskExecutor-3] INFO  service.task.testTask -taskExecutor-3===task run
2018-06-12 16:28:46.027 [taskExecutor-4] INFO  service.task.testTask -taskExecutor-4===task end
2018-06-12 16:28:46.031 [taskExecutor-4] INFO  service.task.testTask -taskExecutor-4===task run

通过日志我们可以看到,再第100条线程也开始执行任务后,没有新的线程再被创建,而是等待有线程执行完后,再开始执行本次任务。

3.配置线程池大小

虽然上面的方式已经解决了我们的问题,但是总觉得不太好,有时候我们需要异步执行任务,但是又不需要这么多的线程的时候,我们可以使用下面的配置来设置线程池的大小
配置文件:

<task:annotation-driven scheduler="testScheduler" />
    <!-- 配置任务线程池和线程池大小 -->
    <task:scheduler id="testScheduler" pool-size="6" />

测试代码:

@Scheduled(cron = "0/2 * * * * ?")
    @Async
    public void doTask() throws InterruptedException {
        logger.info(Thread.currentThread().getName()+"===task run");
        Thread.sleep(6*1_000);
        logger.info(Thread.currentThread().getName()+"===task end");
    }

结果日志:

2018-06-12 18:32:56.032 [taskExecutor-1] INFO  service.task.testTask -taskExecutor-1===task run
2018-06-12 18:32:58.007 [taskExecutor-2] INFO  service.task.testTask -taskExecutor-2===task run
2018-06-12 18:33:00.005 [taskExecutor-3] INFO  service.task.testTask -taskExecutor-3===task run
2018-06-12 18:33:02.008 [taskExecutor-4] INFO  service.task.testTask -taskExecutor-4===task run
2018-06-12 18:33:02.036 [taskExecutor-1] INFO  service.task.testTask -taskExecutor-1===task end
2018-06-12 18:33:04.327 [taskExecutor-2] INFO  service.task.testTask -taskExecutor-2===task end
2018-06-12 18:33:04.328 [taskExecutor-5] INFO  service.task.testTask -taskExecutor-5===task run
2018-06-12 18:33:06.007 [taskExecutor-6] INFO  service.task.testTask -taskExecutor-6===task run
2018-06-12 18:33:06.010 [taskExecutor-3] INFO  service.task.testTask -taskExecutor-3===task end
2018-06-12 18:33:08.459 [taskExecutor-4] INFO  service.task.testTask -taskExecutor-4===task end
2018-06-12 18:33:08.460 [taskExecutor-7] INFO  service.task.testTask -taskExecutor-7===task run
2018-06-12 18:33:10.011 [taskExecutor-8] INFO  service.task.testTask -taskExecutor-8===task run
2018-06-12 18:33:10.332 [taskExecutor-5] INFO  service.task.testTask -taskExecutor-5===task end
2018-06-12 18:33:12.005 [taskExecutor-9] INFO  service.task.testTask -taskExecutor-9===task run
2018-06-12 18:33:12.012 [taskExecutor-6] INFO  service.task.testTask -taskExecutor-6===task end
2018-06-12 18:33:14.904 [taskExecutor-10] INFO  service.task.testTask -taskExecutor-10===task run
2018-06-12 18:33:14.904 [taskExecutor-7] INFO  service.task.testTask -taskExecutor-7===task end

结果和我们预料的并不一样啊,线程数超过了6,这是什么原因呢?

其实如果我们在使用@Async时想使用配置好的线程池,需要为@Async注解添加value属性来制定所用的线程池。
修改后的代码

@Scheduled(cron = "0/2 * * * * ?")
    @Async("testScheduler")
    public void doTask() throws InterruptedException {
        logger.info(Thread.currentThread().getName()+"===task run");
        Thread.sleep(30*1_000);
        logger.info(Thread.currentThread().getName()+"===task end");
    }

结果日志:

2018-06-12 18:54:42.035 [testScheduler-3] INFO  service.task.testTask -testScheduler-3===task run
2018-06-12 18:54:44.010 [testScheduler-2] INFO  service.task.testTask -testScheduler-2===task run
2018-06-12 18:54:46.007 [testScheduler-1] INFO  service.task.testTask -testScheduler-1===task run
2018-06-12 18:54:48.007 [testScheduler-5] INFO  service.task.testTask -testScheduler-5===task run
2018-06-12 18:54:50.005 [testScheduler-4] INFO  service.task.testTask -testScheduler-4===task run
2018-06-12 18:54:52.021 [testScheduler-6] INFO  service.task.testTask -testScheduler-6===task run

2018-06-12 18:55:12.039 [testScheduler-3] INFO  service.task.testTask -testScheduler-3===task end
2018-06-12 18:55:12.044 [testScheduler-3] INFO  service.task.testTask -testScheduler-3===task run
2018-06-12 18:55:14.016 [testScheduler-2] INFO  service.task.testTask -testScheduler-2===task end
2018-06-12 18:55:14.022 [testScheduler-2] INFO  service.task.testTask -testScheduler-2===task run
2018-06-12 18:55:16.289 [testScheduler-1] INFO  service.task.testTask -testScheduler-1===task end
2018-06-12 18:55:16.297 [testScheduler-1] INFO  service.task.testTask -testScheduler-1===task run
2018-06-12 18:55:18.289 [testScheduler-5] INFO  service.task.testTask -testScheduler-5===task end
2018-06-12 18:55:18.293 [testScheduler-5] INFO  service.task.testTask -testScheduler-5===task run
2018-06-12 18:55:20.009 [testScheduler-4] INFO  service.task.testTask -testScheduler-4===task end
2018-06-12 18:55:20.014 [testScheduler-4] INFO  service.task.testTask -testScheduler-4===task run
2018-06-12 18:55:22.165 [testScheduler-6] INFO  service.task.testTask -testScheduler-6===task end
2018-06-12 18:55:22.172 [testScheduler-6] INFO  service.task.testTask -testScheduler-6===task run

这次结果和我们的预计是一样的了,在初始的6条线程使用完毕以后就会等待之前的线程释放啦,同时也可以看到线程池名是我们设置的线程池。

以上就是Spring定时任务@scheduled多线程使用@Async注解示例的详细内容,更多关于Spring @scheduled使用@Async的资料请关注脚本之家其它相关文章!

相关文章

  • Java连接MySQL8.0 JDBC的详细步骤(IDEA版本)

    Java连接MySQL8.0 JDBC的详细步骤(IDEA版本)

    这篇文章主要介绍了Java连接MySQL8.0 JDBC的详细步骤(IDEA版本),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Java通过百度地图API获取定位(普通IP定位)的方法教程

    Java通过百度地图API获取定位(普通IP定位)的方法教程

    这篇文章主要介绍了Java通过百度地图API获取定位的方法教程,首先说明了实现这个功能的需求和初衷,然后详细描述了利用百度地图API实现这个功能的步骤,包括在百度地图开放平台的准备工作、学习官网API文档、修改API的AK配置、Java代码获取定位等,需要的朋友可以参考下
    2024-11-11
  • SpringBoot拦截器实现登录拦截的示例代码

    SpringBoot拦截器实现登录拦截的示例代码

    本文主要介绍了SpringBoot拦截器实现登录拦截,文中根据实例编码详细介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • SpringBoot如何指定某些类优先启动

    SpringBoot如何指定某些类优先启动

    这篇文章主要介绍了SpringBoot如何指定某些类优先启动,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • K8S(Docker)如何优雅的关闭SpringBoot微服务

    K8S(Docker)如何优雅的关闭SpringBoot微服务

    这篇文章主要介绍了K8S(Docker)如何优雅的关闭SpringBoot微服务问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-01-01
  • idea中如何去掉不想commit的文件

    idea中如何去掉不想commit的文件

    这篇文章主要介绍了idea中如何去掉不想commit的文件,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 详解java中的6种单例写法及优缺点

    详解java中的6种单例写法及优缺点

    在java中,单例有很多种写法,面试时,手写代码环节,除了写算法题,有时候也会让手写单例模式,这里记录一下单例的几种写法和优缺点。需要的朋友可以参考下
    2018-11-11
  • IDEA使用Lombok简化POJO代码的示例

    IDEA使用Lombok简化POJO代码的示例

    今天小编就为大家分享一篇关于IDEA使用Lombok简化POJO代码的示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • JUC系列学习工具类CountDownLatch详解

    JUC系列学习工具类CountDownLatch详解

    这篇文章主要介绍了JUC系列学习工具类CountDownLatch详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可任意参考一下
    2022-08-08
  • java实现的小时钟示例分享

    java实现的小时钟示例分享

    这篇文章主要介绍了java实现的小时钟示例,需要的朋友可以参考下
    2014-02-02

最新评论