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);
    }

}
  • 数据加工

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot提供了对线程池集成和支持,可以方便地使用线程池来处理并发任务。下面是使用Spring Boot线程池的基本步骤: 1. 导入依赖:在pom.xml文件中添加Spring Boot对线程池的依赖。例如,对于基于Java的应用,可以添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-async</artifactId> </dependency> ``` 2. 配置线程池:在Spring Boot的配置文件(如application.properties或application.yml)中配置线程池的相关属性。例如,可以配置线程池的核心线程数、最大线程数、队列容量等。以下是一个示例配置: ```properties # 线程池配置 spring.task.execution.pool.core-size=10 spring.task.execution.pool.max-size=20 spring.task.execution.pool.queue-capacity=100 ``` 3. 创建异步方法:在Spring Boot应用中创建一个需要异步执行的方法,并使用`@Async`注解标记该方法。例如: ```java @Service public class MyService { @Async public CompletableFuture<String> asyncMethod() { // 异步执行的方法逻辑 return CompletableFuture.completedFuture("异步方法执行完成"); } } ``` 4. 调用异步方法:在需要调用异步方法的地方,直接调用即可。Spring Boot会自动将方法转化为异步执行。例如: ```java @RestController public class MyController { @Autowired private MyService myService; @GetMapping("/async") public String executeAsyncMethod() { CompletableFuture<String> futureResult = myService.asyncMethod(); // 异步方法返回的是一个CompletableFuture对象,可以通过该对象获取异步方法的执行结果 return "异步方法已启动"; } } ``` 这样,通过以上步骤,你就可以在Spring Boot中使用线程池来处理并发任务了。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值