SpringBoot中使用多线程

在 SpringBoot 应用中,经常会遇到在一个接口中,同时做事情1,事情2,事情3,如果同步执行的话,则本次接口时间取决于事情1 2 3执行时间之和;如果三件事同时执行,则本次接口时间取决于事情1 2 3执行时间最长的那个,合理使用多线程,可以大大缩短接口时间

快速使用

SpringBoot应用中需要添加@EnableAsync注解,来开启异步调用,一般还会配置一个线程池,异步的方法交给特定的线程池完成,如下:

@Slf4j
@Configuration
@EnableAsync
public class ThreadPoolCachedConfig {

    /**
     * 允许线程空闲时间(单位:默认为秒)
     */
    private static final int KEEP_ALIVE_TIME = 10;
    /**
     * 缓冲队列数
     */
    private static final int QUEUE_CAPACITY = 200;
    /**
     * 最大线程数
     */
    private static final int maximnum_pool_size = 200;

    private static final int THREADS = Runtime.getRuntime().availableProcessors() + 1;

    private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
            // -%d不要少
            .setNameFormat("tool-task-name-%d")
            .setDaemon(true)
            .build();

    /**
     * 自定义线程池
     * corePoolSize 核心池的大小
     * maximumPoolSize  线程池最大线程数
     * keepAliveTime  表示线程没有任务执行时最多保持多久时间会终止
     * unit 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性
     *          TimeUnit.DAYS;               //天
     *          TimeUnit.HOURS;             //小时
     *          TimeUnit.MINUTES;           //分钟
     *          TimeUnit.SECONDS;           //秒
     *          TimeUnit.MILLISECONDS;      //毫秒
     *          TimeUnit.MICROSECONDS;      //微妙
     *          TimeUnit.NANOSECONDS;       //纳秒
     * workQueue  一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要
     * threadFactory  用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情
     * handler  表示当拒绝处理任务时的策略,有以下四种取值
     * @return
     */
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        log.info("ThreadPoolCachedConfig.class 自定义线程池执行成功。");
        return new ThreadPoolExecutor(THREADS, 2 * THREADS,
                KEEP_ALIVE_TIME, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(QUEUE_CAPACITY),
                threadFactory, (r, executor) -> {
            // 打印日志,添加监控等
            log.warn("task is rejected!");
        });
    }
}

使用的方式非常简单,在需要异步的方法上加@Async注解。
获取异步方法返回值
当异步方法有返回值时,如何获取异步方法执行的返回结果呢?这时需要异步调用的方法带有返回值CompletableFuture。

CompletableFuture是对Feature的增强,Feature只能处理简单的异步任务,而CompletableFuture可以将多个异步任务进行复杂的组合。如下:


/**
 * 异步线程任务
 *
 * @author wang gang
 * @since 2021-11-09 11:11
 */
@Slf4j
@Service
public class AsyncServiceImpl {

    @Value("${bigData.rainfall.prediction.url}")
    private String bigDataRainfallPredictionUrl;

    @Async("taskExecutor")
    public CompletableFuture<List<BigDataRainfallPredicionPageVO>> bigDateRainfall(BigDataRainfallPredictionQO bigDataRainfallPredictionQO) {
        try{
            log.info("访问地址{}", bigDataRainfallPredictionUrl);
            List<BigDataRainfallPredicionPageVO> bigDataRainfallPredicionPageVOList = obtainBigDataRainfallPredicionPageVOList(
                    bigDataRainfallPredictionQO, null);
            return CompletableFuture.completedFuture(bigDataRainfallPredicionPageVOList);
        }catch (Exception e){
            log.error(e.toString());
            return CompletableFuture.completedFuture(Collections.emptyList());
        }
    }
}    
 private List<BigDataRainfallPredicionPageVO> getBigDataRainfallPredicionPageVOList(LocalDateTime time){
        LocalDate thisDate = time.toLocalDate();
        LocalDateTime time2HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 01:00:00");
        LocalDateTime time2HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 02:00:00");
        //5点整
        LocalDateTime time5HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 04:00:00");
        LocalDateTime time5HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 05:00:00");
        //8点整
        LocalDateTime time8HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 07:00:00");
        LocalDateTime time8HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 08:00:00");
        //11点整
        LocalDateTime time11HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 10:00:00");
        LocalDateTime time11HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 11:00:00");
        //14点整
        LocalDateTime time14HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 13:00:00");
        LocalDateTime time14HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 14:00:00");
        //17点整
        LocalDateTime time17HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 16:00:00");
        LocalDateTime time17HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 17:00:00");
        //20点整
        LocalDateTime time20HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 19:00:00");
        LocalDateTime time20HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 20:00:00");
        //23点整
        LocalDateTime time23HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 22:00:00");
        LocalDateTime time23HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 23:00:00");
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task2Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time2HourFront, time2HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task5Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time5HourFront, time5HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task8Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time8HourFront, time8HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task11Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time11HourFront, time11HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task14Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time14HourFront, time14HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task17Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time17HourFront, time17HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task20Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time20HourFront, time20HourAfter));
        CompletableFuture<List<BigDataRainfallPredicionPageVO>> task23Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time23HourFront, time23HourAfter));
        // 等待所有任务都执行完
        CompletableFuture.allOf(task2Hour, task5Hour, task8Hour, task11Hour, task14Hour, task17Hour, task20Hour, task23Hour).join();
        List<BigDataRainfallPredicionPageVO> bigDataRainfallPredicionPageList = Lists.newArrayList();
        try {
           // 获取每个任务的返回结果
            bigDataRainfallPredicionPageList.addAll(task2Hour.get());
            bigDataRainfallPredicionPageList.addAll(task5Hour.get());
            bigDataRainfallPredicionPageList.addAll(task8Hour.get());
            bigDataRainfallPredicionPageList.addAll(task11Hour.get());
            bigDataRainfallPredicionPageList.addAll(task14Hour.get());
            bigDataRainfallPredicionPageList.addAll(task17Hour.get());
            bigDataRainfallPredicionPageList.addAll(task20Hour.get());
            bigDataRainfallPredicionPageList.addAll(task23Hour.get());
        } catch (Exception e) {
           log.error(e.toString());
        }
        return bigDataRainfallPredicionPageList;
    }

https://zhuanlan.zhihu.com/p/134636915 SpringBoot中如何优雅的使用多线程

上一篇:java8中Date与LocalDateTime的互转


下一篇:Mybatis-Plus系列---【自动填充】