Springboot 集成线程池

项目背景:

     在某次项目中,需要对用户的评分进行计算,评分分5个大项,每个细项又分8个大项,每个细项的数据都来源于另外一个数据查询服务,数据查询服务的接口平均响应时间大概是100ms,查询到数据后还有进行加工处理,评分的要求是要再2s内完成,如果使用常规的顺序执行,时间至少是5 * 8 * 0.1 = 4s,也就是评分接口响应时间超过4s。

问题分析:

      数据的调用接口之间没有依赖关系,因此可以使用多线程来调用数据接口,等所有数据返回后,再统一加工处理,所以问题就变成以下处理点:

  • 如果启用多个线程去调用数据结构
  • 如何统一获取返回值,进行数据加工

显然,可以使用线程池技术。我们项目使用的是springboot框架,因此选择使用spring的

ThreadPoolTaskExecutor 作为线程池的创建器。

示例:

  • 首先,配置一个线程池

     在线程池的配置过程中,线程池的执行规则是,当接受到任务后,如果核心线程的数量未到设置值,会创建新线程去执行,如果线程已经到达设置值,则会将任务存在队列中,如果队列满了且线程未到达最大线程设置,则又会创建新线程去执行,如果线程队列和最大值都满了,则需要设置拒绝策略,具体可以自行百度。

      在这里,有个明细的问题,有些订餐系统中也会遇到,就是请求是爆发式的,比方说,在平时,请求可能就三两个,但是在特定的时间点,会迅速增加到几百、几千甚至更高,这样的情况下,如果核心线程数量设置的很低,那么就面临两个选择,第一是队列小,系统可以很快的提高到最大先线程,但是如果最大线程也处理不过来,就会出现任务被拒绝。第二是队列大,那就会出现很难到队列满,导致一直只有核心线程在执行,效率和性能下降。如果核心线程设置的很高,队列小,同样有请求被拒绝的风险,队列大,依然只有核心线程在执行,但是由于核心线程设置的高,所以效率和性能是可以保证的,但是在平时请求只有三两个的时候,线程池浪费就严重。

     JDK1.6引入了一个新参数 :核心线程允许超时 executor.setAllowCoreThreadTimeOut(true);

可以用来解决该问题,具体使用可以自行百度。


/**
 * @describe: 线程池配置
 * @author: sunlight
 * @date: 2021/7/14 14:31
 */
@Configuration
@EnableAsync
public class ExecutorConfig {
    /**
     * 线程池配置
     *
     * @return
     */
    @Bean("threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(40);
        // 设置最大线程数
        executor.setMaxPoolSize(40);
        // 配置队列大小,缓存100个任务,视业务而定
        executor.setQueueCapacity(100);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(10);
        //核心线程允许超时
        executor.setAllowCoreThreadTimeOut(true);
        //设置默认线程名称
        executor.setThreadNamePrefix("test");
        //初始化
        executor.initialize();
        return executor;
    }
}
  • 其次,配置线程任务,等价于实现runable或者Thread的run方法
    /**
     * 测试
     */
    @Async("threadPoolTaskExecutor")
    public Future<String> test(int i, int j) {
        String name = Thread.currentThread().getName();
        log.info("当前线程,{}", name);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new AsyncResult<String>("test:" + i + j);
    }
  • 调用线程方法,捕获返回值
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private TestService testService;

    /**
     * 执行返回值
     *
     * @return
     */
    @RequestMapping("/test")
    public String test() throws ExecutionException, InterruptedException {
        long begin = System.currentTimeMillis();
        List<Future<String>> futureList = new ArrayList<>(40);
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 8; j++) {
                Future<String> result = testService.test(i, j);
                futureList.add(result);
            }
        }
        //future.get()会引起阻塞,所以不能再上面for循环获取
        for (Future<String> future : futureList) {
            String s = future.get();
            System.out.println("result" + s);
        }

        long end = System.currentTimeMillis();
        return "ok" + (end - begin);
    }

}
  • 数据加工

      结果遍历完之后,就可以进行数据加工了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值