示例:

1、 配置

@EnableAsync
@Configuration
public class TaskExecutorConfiguration {

    @Autowired
    private TaskExecutorProperties taskExecutorProperties;

    @Bean
    public Executor routeGen() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(taskExecutorProperties.getCorePoolSize());
        executor.setQueueCapacity(taskExecutorProperties.getQueueCapacity());
        executor.setMaxPoolSize(taskExecutorProperties.getMaxPoolSize());
        executor.setKeepAliveSeconds(taskExecutorProperties.getKeepAliveSeconds());
        executor.setThreadNamePrefix("gen-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();
        return executor;
    }}

2、 运用(作用于方法上)

@Async("routeGen") public Future<Result<String>> genRouteByCategory(RouteGenDTO routeGenDTO, List<String> cityList, String category){}

3、 异常处理及日志记录

   public Result<String> genRouteByShard(RouteGenDTO routeGenDTO) throws RouteGenException {

        // 根据分片获取城市
        Result<List<String>> cityResult = cityService.queryAllCity();
        if (cityResult == null || !cityResult.isSuccess()) {
            String errorMsg = cityResult == null ? "查询城市没有返回结果" : cityResult.getMsg();
            // 记录查询城市日志
            logQueryCityByShard(RouteJobTypeEnum.ROUTE_GEN, routeGenDTO, null, errorMsg);
            return Result.ofFail(ErrorEnum.QUERY_ERROR.getCode(), errorMsg);
        }

        List<String> cityList = cityResult.getData();
        if (CollectionUtils.isEmpty(cityList)) {
            String errorMsg = "没有查询到城市,无需生成路线";
            // 记录查询城市日志
            logQueryCityByShard(RouteJobTypeEnum.ROUTE_GEN, routeGenDTO, null, errorMsg);
            return Result.ofSuccessMsg(errorMsg);
        }

        // 记录查询城市日志
        logQueryCityByShard(RouteJobTypeEnum.ROUTE_GEN, routeGenDTO, JSON.toJSONString(cityList), null);

        Map<String, Future<Result<String>>> categoryFutureMap = new ConcurrentHashMap<>();

        // 禁用的类别列表
        List<String> disableCategoryList = null;
        String disableCategory = genProperties.getDisableCategory();
        if (StringUtils.isNotBlank(disableCategory)) {
            String[] disableCategoryArr = disableCategory.split(",");
            disableCategoryList = Stream.of(disableCategoryArr).collect(Collectors.toList());
        }

        // 根据类别生成路线
        // 每个类别一个线程
        for (RouteCategoryEnum routeCategory : RouteCategoryEnum.values()) {
            // 跳过禁用的类别
            if (CollectionUtils.isNotEmpty(disableCategoryList) && disableCategoryList.contains(routeCategory.getType())) {
                // 记录生成日志
                logGenRouteByCategory(RouteJobTypeEnum.ROUTE_GEN, routeGenDTO, routeCategory.getType(), routeCategory.getType() + "类别被禁用,无需生成");
                continue;
            }

            // 根据城市和类别生成路线
            Future<Result<String>> categoryFuture = routeGenService.genRouteByCategory(routeGenDTO, cityList, routeCategory.getType());
            categoryFutureMap.put(routeCategory.getType(), categoryFuture);
        }

        String lastErrorMsg = null;

        for (Map.Entry<String, Future<Result<String>>> entry : categoryFutureMap.entrySet()) {

            String errorMsg = null;

            String category = entry.getKey();
            Future<Result<String>> futureResult = entry.getValue();
            if (futureResult != null) {
                try {
                    if (futureResult.isCancelled()) {
                        errorMsg = "线程被取消";
                        log.error("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 处理失败,错误描述:{}",
                                routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), errorMsg);
                        continue;
                    }
                    Result<String> result = futureResult.get(CommonConfConstants.FUTURE_CATEGORY_WAIT_TIME, TimeUnit.SECONDS);

                    if (result == null || !result.isSuccess()) {
                        errorMsg = result == null ? category + "没有返回处理结果" : result.getMsg();
                        log.error("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 处理失败,错误描述:{}",
                                routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), errorMsg);
                    } else {
                        log.info("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 生成成功",
                                routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), result);
                    }
                } catch (TimeoutException e) {
                    errorMsg = category + "生成路线 线程处理超时:" + e.getMessage();
                    log.error("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 线程处理超时",
                            routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), e);
                } catch (InterruptedException e) {
                    errorMsg = category + "生成路线 线程中断异常:" + e.getMessage();
                    log.error("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 线程中断异常",
                            routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), e);
                } catch (ExecutionException e) {
                    errorMsg = category + "生成路线 线程执行异常:" + e.getMessage();
                    log.error("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 线程执行异常",
                            routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), e);
                }

            } else {
                errorMsg = category + "生成路线 线程返回结果为null";
                log.error("分片项{} 分片总数{} 生成 {} 路线 任务开始时间:{} 处理失败,错误描述:{}",
                        routeGenDTO.getShardItem(), routeGenDTO.getShardTotal(), category, routeGenDTO.getStartTime(), errorMsg);
            }

            // 记录生成日志
            logGenRouteByCategory(RouteJobTypeEnum.ROUTE_GEN, routeGenDTO, category, errorMsg);

            if (StringUtils.isNotBlank(errorMsg)) {
                lastErrorMsg = errorMsg;
            }
        }

        if (StringUtils.isNotBlank(lastErrorMsg)) {
            return Result.ofFail(ErrorEnum.GEN_ROUTE_FAILURE.getCode(), lastErrorMsg);
        }

        return Result.ofSuccess("路线生成成功");
    }
private void logQueryCityByShard(RouteJobTypeEnum routeJobTypeEnum, RouteGenDTO routeGenDTO, String cityJson, String errorMsg) {    String logFormat;    String logMsg;    if (StringUtils.isBlank(errorMsg)) {        logFormat = "查询城市成功,城市信息:%s";        logMsg = String.format(logFormat, cityJson);    } else {        logMsg = errorMsg;    }

    String operationObject = routeJobTypeEnum.getType() + "_job_"            + routeGenDTO.getShardItem() + "_"            + TimeUtil.dateTimeToStr(routeGenDTO.getStartTime(), DateFormatConstants.DATE_TIME_COMPACT_FORMAT);

    LogInfoDTO logInfoDTO = new LogInfoDTO();    logInfoDTO.setBusinessName(routeJobTypeEnum.getDesc());    logInfoDTO.setOperationName("根据分片查询城市");    logInfoDTO.setOperationObject(operationObject);    logInfoDTO.setOperationDesc(logMsg);    LogUtil.log(logInfoDTO);}
 

springboot+@async异步线程池的配置及应用的更多相关文章

  1. SpringBoot使用异步线程池实现生产环境批量数据推送

    前言 SpringBoot使用异步线程池: 1.编写线程池配置类,自定义一个线程池: 2.定义一个异步服务: 3.使用@Async注解指向定义的线程池: 这里以我工作中使用过的一个案例来做描述,我所在 ...

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

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

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

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

  4. Springboot的异步线程池

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

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

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

  6. Spring Boot中使用@Async的时候,千万别忘了线程池的配置!

    上一篇我们介绍了如何使用@Async注解来创建异步任务,我可以用这种方法来实现一些并发操作,以加速任务的执行效率.但是,如果只是如前文那样直接简单的创建来使用,可能还是会碰到一些问题.存在有什么问题呢 ...

  7. springboot(十九)-线程池的使用

    我们常用ThreadPoolExecutor提供的线程池服务,springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行. 话不多说,编码开始: 1.创建spri ...

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

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

  9. 记录ThreadPoolTaskExecutor线程池的在项目中的实际应用,讲解一下线程池的配置和参数理解。

    前言:最近项目中与融360项目中接口对接,有反馈接口(也就是我们接收到请求,需要立即响应,并且还要有一个接口推送给他们其他计算结果),推送过程耗时.或者说两个接口不能是同时返回,有先后顺序. 这时我想 ...

随机推荐

  1. Swift中关于集合计算的几种函数记录(intersect、symmetricDifference、union、subtract)

    很久之前用过一次,后来就忘了...扎心,现在记录一下 PS:这几种函数其实不限于swift内的,在JavaScript.python.DB等其他语言,应该也有类似用法,这里我只简单讲了在swift内的 ...

  2. MATLAB 统计元素出现的次数

    可以使用 hist 函数: A = [1 2 8 8 1 8 2 1 8 2 1]; count = hist(A,unique(A)) count的结果与unique(A)对应.

  3. 整体C#与Sql培训内容及结构

    图如果看不清可以右键存图片到本地

  4. Nios II 程序固化(如何下载elf文件)

    Nios II 程序固化(如何下载elf文件) 2018年10月15日 21:37:32 瓜儿不甜 阅读数:723    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...

  5. [HDFS Manual] CH8 HDFS Snapshots

    HDFS Snapshots HDFS Snapshots 1. 概述 1.1 Snapshottable目录 1.2 快照路径 2. 带快照的更新 3. 快照操作 3.1 管理操作 3.2 用户操作 ...

  6. 双网卡双线路DNS解析分析

    在企业网络维护过程中我们经常会遇到这样或那样的奇怪问题,而很多问题需要有深厚的理论知识才能解决.而随着网络的飞速发展越来越多的中小企业开始尝试通过多条线路来保证网络的畅通,一方面双网卡下的双线接入可以 ...

  7. static在类中的功能

    有时候类需要它的一些成员与类本身直接相关,而不是与类的各个对象保持关联. 例如一个银行账户类可能需要一个数据成员来表示当前的利率.在此例中,我们希望利率与类关联,而非与类的每个对象关联.从实现效率上来 ...

  8. [转]Jsoup(一)Jsoup详解(官方)

    原文地址:http://www.cnblogs.com/zhangyinhua/p/8037599.html 一.Jsoup概述 1.1.简介     jsoup 是一款Java 的HTML解析器,可 ...

  9. 字符集之在UTF-8中,一个汉字为什么需要三个字节?

    (一)在UTF-8中,一个汉字为什么需要三个字节? UNICODE是万能编码,包含了所有符号的编码,它规定了所有符号在计算机底层的二进制的表示顺序.有关Unicode为什么会出现就不叙述了,Unico ...

  10. Java中Comparable和Comparator区别

    很好的一篇博客:http://blog.csdn.net/jq_ak47/article/details/61203817 http://www.cnblogs.com/cmxwt/p/6215253 ...