前言

SpringBoot使用异步线程池:

1、编写线程池配置类,自定义一个线程池;

2、定义一个异步服务;

3、使用@Async注解指向定义的线程池;

这里以我工作中使用过的一个案例来做描述,我所在公司是医疗行业,敏感数据需要上报到某监管平台,所以有一个定时任务在流量较小时(一般是凌晨后)执行上报行为。但特殊时期会存在一定要在工作时间大批量上报数据的情况,且要求短时间内就要完成,此时就考虑写一个专门的异步上报接口手动执行,利用线程池上报,极大提高了速度。


  • 编写线程池配置类

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor; /**
    * 类名称:ExecutorConfig
    * ********************************
    * <p>
    * 类描述:线程池配置
    *
    * @author guoj
    * @date 2021-09-07 09:00
    */
    @Configuration
    @EnableAsync
    @Slf4j
    public class ExecutorConfig {
    /**
    * 定义数据上报线程池
    * @return
    */
    @Bean("dataCollectionExecutor")
    public Executor dataCollectionExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数量:当前机器的核心数
    executor.setCorePoolSize(
    Runtime.getRuntime().availableProcessors()); // 最大线程数
    executor.setMaxPoolSize(
    Runtime.getRuntime().availableProcessors() * 2); // 队列大小
    executor.setQueueCapacity(Integer.MAX_VALUE); // 线程池中的线程名前缀
    executor.setThreadNamePrefix("sjsb-"); // 拒绝策略:直接拒绝
    executor.setRejectedExecutionHandler(
    new ThreadPoolExecutor.AbortPolicy()); // 执行初始化
    executor.initialize(); return executor;
    } }

    PS:

    1)、需要注意,这里一定要自己定义ThreadPoolTaskExecutor线程池,否则springboot的异步注解会执行默认线程池,存在线程阻塞导致CPU飙高及内存溢出的风险。这一点可以参考阿里开发手册,线程池定义这块明确提到了这一点;

    2)、在@Bean注解中定义线程池名称,后面异步注解会用到。


  • 编写异步服务

    /**
    * 异步方法的服务, 不影响主程序运行。
    */
    @Service
    public class AsyncService { private final Logger log = LoggerFactory.getLogger(AsyncService.class); /**
    * 发送短信
    */
    @Async("sendMsgExecutor")
    public void sendMsg(String access_token, Consult item, Map<String, String> configMap) {
    // 此处编写发送短信业务
    // 1、buildConsultData();
    // 2、sendMsg();
    } /**
    * 发送微信订阅消息
    */
    @Async
    public void sendSubscribeMsg(String access_token, Consult item, Map<String, String> configMap) {
    // 此处编写发送微信订阅消息业务
    // 1、buildConsultData();
    // 2、sendSubscribeMsg();
    } /**
    * 数据并上报
    */
    @Async("dataCollectionExecutor")
    public void buildAndPostData(String access_token, Consult item, Map<String, String> configMap) {
    // 此处编写上报业务,如拼接数据,然后执行上报。
    // 1、buildConsultData();
    // 2、postData();
    }
    }
PS:
1)、以上是代码片段,个人经验认为专门定义一个异步service存放各个异步方法最佳,这样可以避免编码时一些误操作比如异步方法不是void或者是private修饰,导致@Async注解失效的情况,同时可以安排每个注解指向不同的自定义线程池更加灵活;
2)、@Async注解中的名称就是上面定义的自定义线程池名称,这样业务执行时就会从指定线程池中获取异步线程。

  • 异步批量上报数据

    @Autowired
    private AsyncService asyncService; /**
    * 手动上报问诊记录,线程池方式。
    */
    public void manualUploadConsultRecordsAsync(String channel, Date startTime, Date endTime) { // 查询指定时间内的问诊记录
    List<Consult> consultList = consultService
    .findPaidListByChannelAndTime(channel, startTime, endTime, configMap.get("serviceId")); if (!CollectionUtils.isEmpty(consultList)) { log.debug("[SendWZDataService][manualUploadConsultRecordsAsync]>>>> 手动上报问诊记录, 一共[{}]条", consultList.size()); consultList.forEach((item) -> {
    try {
    // 异步调用,使用线程池。
    asyncService.buildAndPostData(access_token, item, configMap);
    } catch (Exception ex) {
    log.error("[SendWZDataService][manualUploadConsultRecordsAsync]>>>> 手动上报问诊记录发生异常: ", ex);
    }
    }); }
    }

    • 总结

      以上方式已经在生产环境运行,在工作时间内执行过很多次,一次数万条记录基本是几分钟内就全部上报完毕,而正常循环遍历时一次大概需要半个小时左右。
      线程池的使用方式往往来源于业务场景,如果类似的业务不存在紧急处理的情况,大体还是以任务调度执行为主,因为更安全。如果存在紧急处理的情况,那么使用SpringBoot+线程池的方式不仅能节省非常多的时间,且不占用主线程的执行空间。
      喜欢就点个关注吧~~

SpringBoot使用异步线程池实现生产环境批量数据推送的更多相关文章

  1. Springboot的异步线程池

    1:定义线程池 @EnableAsync @Configuration class TaskPoolConfig { @Bean("taskExecutor") public Ex ...

  2. springboot+@async异步线程池的配置及应用

    示例: 1. 配置 @EnableAsync @Configuration public class TaskExecutorConfiguration { @Autowired private Ta ...

  3. spring boot:使用async异步线程池发送注册邮件(spring boot 2.3.1)

    一,为什么要使用async异步线程池? 1,在生产环境中,有一些需要延时处理的业务场景: 例如:发送电子邮件, 给手机发短信验证码 大数据量的查询统计 远程抓取数据等 这些场景占用时间较长,而用户又没 ...

  4. springBoot服务整合线程池ThreadPoolTaskExecutor与@Async详解使用

    ThreadPoolExecutor:=======这个是java自己实现的线程池执行类,基本上创建线程池都是通过这个类进行的创建.ThreadPoolTaskExecutor:========这个是 ...

  5. 使用C++11 开发一个半同步半异步线程池

    摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请 ...

  6. 使用C++11实现一个半同步半异步线程池

    前言 C++11之前我们使用线程需要系统提供API.posix线程库或者使用boost提供的线程库,C++11后就加入了跨平台的线程类std::thread,线程同步相关类std::mutex.std ...

  7. c++11 实现半同步半异步线程池

    感受: 随着深入学习,现代c++给我带来越来越多的惊喜- c++真的变强大了. 半同步半异步线程池: 事实上非常好理解.分为三层 同步层:通过IO复用或者其它多线程多进程等不断的将待处理事件加入到队列 ...

  8. Spring Boot系列二 Spring @Async异步线程池用法总结

    1. TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncT ...

  9. (原创)C++半同步半异步线程池2

    (原创)C++半同步半异步线程池 c++11 boost技术交流群:296561497,欢迎大家来交流技术. 线程池可以高效的处理任务,线程池中开启多个线程,等待同步队列中的任务到来,任务到来多个线程 ...

随机推荐

  1. 【LeetCode】557. Reverse Words in a String III 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 题目地址:ht ...

  2. 第二十四个知识点:描述一个二进制m组的滑动窗口指数算法

    第二十四个知识点:描述一个二进制m组的滑动窗口指数算法 简单回顾一下我们知道的. 大量的密码学算法的大数是基于指数问题的安全性,例如RSA或者DH算法.因此,现代密码学需要大指数模幂算法的有效实现.我 ...

  3. LT7211替代芯片|低BOM成本替代LT7211 EDP转LVDS转换设计芯片CS5211

    LT7211B是一种用于虚拟现实/显示应用的TYPE-C/DP1.2转LVDS转换芯片.LT7211B 对于DP1.2输入,LT7211B可以配置为1.2.4车道,还支持车道交换功能.自适应均衡使其适 ...

  4. JUC之集合中的线程安全问题

    集合线程安全问题 JDK Version:9 首先说下集合线程安全是什么:当多个线程对同一个集合进行添加和查询的时候,出现异常错误. 复现例子: package com.JUC; import jav ...

  5. Java初学者作业——编写JAVA程序,计算跳水运动员本次动作的最终得分。

    返回本章节 返回作业目录 需求说明: 编写JAVA程序,计算跳水运动员本次动作的最终得分. 规则如下: 在跳水比赛中,共有六位裁判对运动员所完成的动作进行评分,每位裁判的评分在0-10之间,运动员最终 ...

  6. Java初学者作业——编写Java程序,输入一个学生的5门课程的成绩,求其平均分。

    返回本章节 返回作业目录 需求说明: 编写Java程序,输入一个学生的5门课程的成绩,求其平均分.计算平均成绩,需要将每一门课程的成绩逐步累加到总成绩中,使用 for 循环实现,然后求出平均分. 实现 ...

  7. Nginx部署及Web基础

    目录 Nginx部署及Web基础 Nginx简介 Nginx特点 Web服务 Web服务器软件 Nginx和Apache对比图 部署Nginx yum安装 编译安装 平滑增加Nginx模块 Nginx ...

  8. 首次分享,大厂资深测试做Api接口自动化测试的关键思路都在这里了

    引言 与UI相比,接口一旦研发完成,通常变更或重构的频率和幅度相对较小.因此做接口自动化的性价比更高,通常运用于迭代版本上线前的回归测试中. 手工做接口测试,测试数据和参数都可以由测试人员手动填写和更 ...

  9. spring boot 热部署 实现 前端部分热更新 详细操作

    1.前言 在以前的随笔[https://www.cnblogs.com/c2g5201314/p/12275243.html] 里面已经讲解过了 idea 如何在 springMVC 项目 实现 前端 ...

  10. react中虚拟DOM

    简单来说虚拟DOM就是一个js对象,相对于真实dom来做比较更节约性能,虚拟DOM执行过程如下