线程池异步处理(并行提高效率)

线程池配置

package com.fast.boot.framework.config;

import com.fast.boot.common.config.ThreadPoolProperties;
import com.fast.boot.common.utils.ThreadsUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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 javax.annotation.Resource;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池配置
 *
 * @author ming
 * @date 2023-01-03 10:51
 **/
@EnableAsync // 开启异步
@Configuration
@EnableConfigurationProperties(value = ThreadPoolProperties.class)
public class ThreadPoolConfig {

    @Resource
    private ThreadPoolProperties threadPoolProperties;

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix(threadPoolProperties.getThreadCustomPrefix());
        executor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
        executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
        executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }

    /*
    tasks :每秒的任务数,假设为500~1000
    taskcost:每个任务花费时间,假设为0.1s
    responsetime:系统允许容忍的最大响应时间,假设为1s
    做几个计算
    corePoolSize = 每秒需要多少个线程处理?
    threadcount = tasks/(1/taskcost) = tasks*taskcout = (500 ~ 1000)*0.1 = 50~100 个线程。
    corePoolSize设置应该大于50。
    根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可。
    queueCapacity = (coreSizePool/taskcost)responsetime
     */

    /**
     * 执行周期性或定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(threadPoolProperties.getCorePoolSize(),
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                ThreadsUtils.printException(r, t);
            }
        };
    }
}

模拟测试

package com.fast.boot.web.controller.common;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.fast.boot.common.annotation.GlobalResult;
import com.fast.boot.mail.service.MailService;
import com.fast.boot.model.AjaxResult;
import com.fast.boot.model.Result;
import com.fast.boot.service.sdk.CommonService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 通用请求处理
 *
 * @author ming
 * @date 2022-12-27 18:56
 */
@RestController
@RequestMapping("/common")
@RequiredArgsConstructor
@Slf4j
public class CommonController {

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @ApiOperation(value = "测试接口")
    @GetMapping("test")
    public AjaxResult<?> test(String key, Integer len) {
        log.info("测试 :{}", key);
        test(len);
        return AjaxResult.success("测试");
    }

    @ApiOperation(value = "测试接口3")
    @GetMapping("test3")
    @GlobalResult
    public Integer test3(String key, Integer count) {
        log.info("异步测试 :{}", key);
        List<String> searchList = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            searchList.add(System.currentTimeMillis() + "" + i);
        }
        List<Callable<Map<String, Object>>> tasks = searchList.stream()
                .map(item -> (Callable<Map<String, Object>>) () -> buildResultMap(item))
                .collect(Collectors.toList());
        List<Map<String, Object>> res = execQuery(tasks);
        log.info("test3异步结果:" + JSON.toJSONString(res));
        return 3;
    }

    public void test(Integer count) {
        log.info("执行并行、串行测试: count=" + count);
        // 并行流测试
        List<Long> searchList = new ArrayList<>();
        for (long i = count; i > 0; i--) {
            searchList.add(System.currentTimeMillis() + i);
        }

        long asyncStart = System.currentTimeMillis();
        List<String> completableFutureList = searchList.parallelStream()
                .map(first -> CompletableFuture.supplyAsync(() -> getSearchResult(first), threadPoolTaskExecutor))
                .map(second -> second.thenApply(this::queryB))
                .map(third -> third.thenApply(this::queryB))
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        long elapsedTime = System.currentTimeMillis() - asyncStart;
        log.info("并行:" + elapsedTime);
        // completableFutureList.forEach(System.out::println);

        asyncStart = System.currentTimeMillis();
        List<String> completableFutureList2 = searchList.parallelStream()
                .map(this::getSearchResult)
                .map(this::queryB)
                .map(this::queryB)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        elapsedTime = System.currentTimeMillis() - asyncStart;
        log.info("串行:" + elapsedTime);

        // completableFutureList2.forEach(System.out::println);
        // CompletableFuture.supplyAsync().thenApply() 使用上一步的结果继续执行,使用的线程池类型不同ExecutorService executorService = Executors.newSingleThreadExecutor();
    }

    public String getSearchResult(long orderId) {
        // 模拟外部接口耗时
        ThreadUtil.safeSleep(50);
        //do some your business
        return "getSearchResult--测试:" + orderId;
    }

    public String queryB(String s) {
        // 模拟外部接口耗时
        ThreadUtil.safeSleep(50);
        return "queryB" + s;
    }

    /**
     * 异步执行(实际效果比parallelStream更优)
     *
     * @param tasks 异步任务
     * @return java.util.List<java.lang.Object>
     * @author ext.wenzhongming1
     * @since 2023/2/3 11:17
     */
    protected <U> List<U> execQuery(List<Callable<U>> tasks) {
        try {
            List<Future<U>> results = threadPoolTaskExecutor.getThreadPoolExecutor().invokeAll(tasks);

            return results.stream().map(item -> {
                try {
                    return item.get();
                } catch (Exception e) {
                    log.error("【execQuery】异步执行结果获取异常", e);
                }
                return null;
            }).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("【execQuery】异步执行异常", e);
        }
        return Collections.emptyList();
    }

    /**
     * 需要异步的业务代码,模拟接口耗时
     *
     * @param s 入参
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @author ext.wenzhongming1
     * @since 2023/2/6 10:24
     */
    private Map<String, Object> buildResultMap(String s) {
        Map<String, Object> map = new HashMap<>();
        map.put("testId", s);
        ThreadUtil.safeSleep(RandomUtil.randomInt(100));
        return map;
    }
}

posted @ 2023-02-08 17:25  itwetouch  阅读(154)  评论(0编辑  收藏  举报