spring boot整合线程池

本文介绍了如何在SpringBoot项目中配置和使用线程池,包括核心线程数、最大线程数、空闲线程存活时间等参数设置,并展示了如何通过配置文件动态调整。此外,还详细阐述了线程池的工作原理和拒绝策略,并提供了创建线程池的示例代码。

多线程在项目中运用的很多,比如告警消息的发送,需要将告警信息推送到运维人员的微信、邮件、短信等等,电商项目提交订单需要查询商品基础信息、库存、优惠信息、优惠券、用户积分、收货地址、快递信息等等;其中涉及到的流程比较长,如果采用单线程的方式总计耗时将是所有流程的时间的总和;采用多线程的方式最好的状态是最长流程的时间,如果这些流程有依赖关系则可以采用CompletableFuture进行编排;因此对于接口响应时长有要求的业务多线程的运用必不可少。
一般来说一个项目里面有一个线程池就够了,需要用到多线程的地方使用该线程池就行;但不排除有些项目会使用到多个线程池,比如项目核心功能使用一个线程池,告警、对接三方等等周边业务使用一个线程池。最好将线程池的参数配置放在配置文件中比如yaml文件,如果采用了nacos等配置中心组件也可以放在其中,如果项目采用了k8s也可以将配置信息放在ConfigMap中。

先看看线程池七大参数:

int corePoolSize:核心线程数即该线程池不会被销毁的线程数量
int maximumPoolSize:最大线程数即该线程池可同时存在的最大线程数量,该参数不会比corePoolSize小
long keepAliveTime:空闲线程存活时长,当线程池中线程数量大于核心线程数之后,并且存在空闲线程的情况下,当空闲时长超过该时长之后则会被销毁,直到剩余线程数量和corePoolSize数量相等
TimeUnit unit:keepAliveTime的单位
BlockingQueue<Runnable> workQueue:等待队列,当线程池创建之后,接受任务的时候如果线程池中线程数量小于核心线程数则会创建线程来执行该任务,如果大于等于核心线程数之后新提交的任务会进入到workQueue中,等待线程池中的空闲线程从workQueue取出执行任务,如果workQueue满了之后,当线程池中线程数小于maximumPoolSize的情况下则会创建线程来执行该任务,如果线程池中的线程数量等于maximumPoolSize则会执行拒绝策略
ThreadFactory threadFactory:创建线程使用的工厂
RejectedExecutionHandler handler:拒绝策略,当线程池满负荷运转之后新进来的任务的处理方式

在这里插入图片描述
spring boot整合线程池
1、创建线程池配置类
@ConfigurationProperties(“thread.pool”):自动读取配置文件中thread.pool开头的配置信息绑定到对应的字段上,绑定规则是将配置文件中的配置去除掉-转为小驼峰就是对应的类中的字段,如thread.pool.core-pool-size对应字段就是corePoolSize。
@Component:将该类交由spring容器管理
@Data:为字段生成get、set方法

package com.test.springboot.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @version 1.0
 * @Description 线程池配置类
 */
@Component
@Data
@ConfigurationProperties("thread.pool")
public class ThreadPoolConfig {

    private Integer corePoolSize = 3;

    private Integer maximumPoolSize = 6;

    private Integer keepAliveTime = 30;

    private Integer workQueueSize = 10000;

}

2、配置文件中定义配置,当配置文件和配置类都定义了对应属性采用配置文件中的属性值

thread:
  pool:
    core-pool-size: 5
    maximum-pool-size: 10
    keep-alive-time: 60
    work-queue-size: 10000

3、创建线程池

package com.test.springboot.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

/**
 * @version 1.0
 * @Description 线程池工厂
 */
@Configuration
public class ThreadPoolFactory {

    @Autowired
    private ThreadPoolConfig threadPoolConfig;


    @Bean("threadPool")
    public ThreadPoolExecutor createpool(){
        return new ThreadPoolExecutor(threadPoolConfig.getCorePoolSize(),threadPoolConfig.getMaximumPoolSize(),threadPoolConfig.getKeepAliveTime(), TimeUnit.SECONDS,new LinkedBlockingQueue<>(threadPoolConfig.getWorkQueueSize()), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
    }
}

到此,当我们启动spring boot项目的时候会在spring容器中注入一个核心线程数为5、最大线程数10、空闲时间60s、等待队列长度10000、拒绝策略为抛出异常、名称为threadPool的线程池。
在需要用到线程池的类中通过@Resource或@Autowired注入即可使用。

<think>首先,用户的问题是:不使用@Async注解,通过Spring Boot整合线程池和CountDownLatch实现批量业务处理并返回处理结果的方法。 关键点: - **不使用@Async注解**:用户明确指定不使用Spring的@Async异步注解。 - **整合线程池**:需要使用线程池来管理线程。 - **CountDownLatch**:用于等待所有线程完成。 - **返回处理结果**:需要返回批量业务处理的结果。 从提供的引用中,我可以提取相关信息: - 引用[3]展示了使用@Async注解和CountDownLatch来处理批量业务,但用户不想用@Async。 - 引用[4]提到使用CountDownLatch和Future来处理异步任务,特别指出Future.get()是阻塞的,可以用于等待线程完成并获取结果。 我的回答需要: - 使用中文。 - 结构清晰,逐步解释。 - 遵循系统级指令:行内数学表达式用$...$,独立公式用$$...$$(但在这个上下文中可能不适用),LaTeX语法正确。 - 生成相关问题。 - 引用末尾添加引用标识。 核心思路: 既然不使用@Async,我可以直接使用Java的ExecutorService来创建线程池,然后提交任务(Callable或Runnable)。每个任务返回一个Future对象,我可以收集这些Future对象,然后使用CountDownLatch来等待所有任务完成,最后从Future对象中获取处理结果。 步骤: 1. **创建线程池**:使用Executors.newFixedThreadPool或其他方式创建线程池。 2. **定义任务**:实现Callable接口,每个任务执行业务逻辑并返回结果。 3. **提交任务**:提交任务到线程池,收集Future对象。 4. **使用CountDownLatch**:初始化CountDownLatch为任务数量,每个任务完成后计数减一。 5. **等待所有任务完成**:在主线程中调用CountDownLatch.await()等待。 6. **获取结果**:从Future对象中获取每个任务的结果。 在引用[4]中提到,如果使用Callable和Future,Future.get()本身是阻塞的,所以CountDownLatch可能不是必需的,因为Future.get()会等待任务完成。但用户指定使用CountDownLatch,所以我需要结合使用。 优化: - 使用CountDownLatch来同步所有任务完成。 - 每个任务完成后计数减一。 - 主线程等待latch归零。 - 然后遍历Future对象获取结果。 代码示例: 既然用户参考了Spring Boot,我需要在Spring Boot上下文中实现。例如,在服务类中注入线程池Bean。 实现步骤: 1. **配置线程池Bean**:在Spring Boot的配置类中定义ExecutorService bean。 2. **服务类中使用线程池**:注入ExecutorService,定义方法处理批量任务。 3. **提交任务和同步**:提交Callable任务,记录Future,使用CountDownLatch。 代码结构: ```java import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class BatchService { private final ExecutorService executorService; public BatchService(ExecutorService executorService) { this.executorService = executorService; } public List<Result> processBatch(List<Input> inputs) throws InterruptedException { int size = inputs.size(); CountDownLatch latch = new CountDownLatch(size); List<Future<Result>> futures = new ArrayList<>(); for (Input input : inputs) { Callable<Result> task = () -> { try { // 执行业务处理 Result result = doBusiness(input); return result; } finally { latch.countDown(); // 无论成功与否,都计数减一 } }; futures.add(executorService.submit(task)); } latch.await(); // 等待所有任务完成 // 获取结果 List<Result> results = new ArrayList<>(); for (Future<Result> future : futures) { try { results.add(future.get()); // 此时get应该立即返回,因为任务已完成 } catch (ExecutionException e) { // 处理异常 results.add(null); // 或其他错误处理 } } return results; } private Result doBusiness(Input input) { // 实际业务逻辑 return new Result(); } } ``` 在上述代码中: - 每个任务是一个Callable。 - 提交后,添加Future到列表。 - 在任务finally块中调用latch.countDown()。 - 主线程await latch。 - 然后遍历Future获取结果。 注意:Future.get()可能抛出异常,需要处理。 在Spring Boot中配置线程池: ```java @Configuration public class ThreadPoolConfig { @Bean public ExecutorService executorService() { return Executors.newFixedThreadPool(10); // 自定义参数 } } ``` 然后注入到服务类中。 用户要求返回处理结果,所以Callable返回结果对象。 引用相关: - 引用[4]讨论了Future.get()的阻塞行为,所以我可以在响应中提及这一点。 生成相关问题: 在回答结束后,生成相关问题。 回答结构: 1. **引言**:简述不使用@Async的原因和实现思路。 2. **步骤详解**:分步解释如何实现。 3. **代码示例**:提供Spring Boot中的代码片段。 4. **注意事项**:如异常处理、资源管理等。 5. **生成相关问题**。 </think>### 不使用@Async注解整合线程池和CountDownLatch实现批量处理并返回结果 在Spring Boot中,无需依赖`@Async`注解,可通过Java原生线程池(`ExecutorService`)和`CountDownLatch`实现批量业务处理并返回结果。核心思路是: 1. **手动配置线程池**:替代`@Async`的自动线程池管理。 2. **定义任务为`Callable`**:使每个任务能返回处理结果。 3. **结合`CountDownLatch`同步**:确保所有任务完成后收集结果。 4. **使用`Future`获取返回值**:通过`Future.get()`阻塞式获取结果。 以下是详细实现步骤和代码示例(基于Spring Boot环境): --- ### 一、实现步骤 1. **配置线程池Bean** 在Spring Boot配置类中定义线程池Bean,替代`@Async`的默认线程池: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.*; @Configuration public class ThreadPoolConfig { @Bean public ExecutorService batchThreadPool() { // 创建固定大小线程池(参数根据需求调整) return Executors.newFixedThreadPool(10); } } ``` 2. **定义业务服务类** 注入线程池,实现批量处理方法: ```java import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; @Service public class BatchService { @Resource private ExecutorService batchThreadPool; // 注入线程池 /** * 批量处理业务并返回结果 * @param dataList 输入数据列表 * @return 处理结果列表 */ public List<String> processBatch(List<String> dataList) throws InterruptedException { int taskCount = dataList.size(); CountDownLatch latch = new CountDownLatch(taskCount); List<Future<String>> futures = new ArrayList<>(); // 存储Future对象 // 提交任务到线程池 for (String data : dataList) { Callable<String> task = () -> { try { return doBusiness(data); // 执行业务逻辑 } finally { latch.countDown(); // 确保计数减一(无论成败) } }; futures.add(batchThreadPool.submit(task)); } latch.await(); // 阻塞等待所有任务完成 return collectResults(futures); // 收集结果 } /** * 具体业务逻辑(示例) */ private String doBusiness(String data) { // 模拟业务处理(如数据库操作) return "Processed: " + data; } /** * 从Future对象提取结果 */ private List<String> collectResults(List<Future<String>> futures) { List<String> results = new ArrayList<>(); for (Future<String> future : futures) { try { results.add(future.get()); // 获取结果(此时任务已完成) } catch (ExecutionException e) { results.add("Error: " + e.getCause().getMessage()); } } return results; } } ``` 3. **控制器层调用** 在Controller中调用批量处理方法: ```java import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Arrays; import java.util.List; @RestController public class BatchController { @Resource private BatchService batchService; @PostMapping("/batch-process") public List<String> handleBatch(@RequestBody List<String> inputs) throws InterruptedException { return batchService.processBatch(inputs); } } ``` --- ### 二、关键机制解析 1. **线程池管理** - 使用`ExecutorService.submit(Callable)`提交任务,每个任务返回`Future`对象。 - 替代`@Async`的异步代理机制,减少Spring AOP的依赖[^3]。 2. **CountDownLatch同步** - `latch.await()`阻塞主线程,直到所有任务的`latch.countDown()`被调用。 - 确保在收集结果前所有任务已完成,避免主线程提前退出[^4]。 3. **结果返回** - `Future.get()`在任务完成后立即返回结果(因`latch`已确保任务完成)。 - 异常处理:通过`ExecutionException`捕获任务执行中的错误。 4. **资源释放** - 推荐在应用关闭时关闭线程池(例如实现`DisposableBean`): ```java @PreDestroy public void destroy() { batchThreadPool.shutdown(); } ``` --- ### 三、注意事项 1. **线程安全**:确保业务逻辑`doBusiness()`是线程安全的(无共享可变状态)。 2. **超时控制**:可在`latch.await(timeout, unit)`添加超时参数,防止死锁。 3. **批量拆分**:若数据量极大(如$n > 10^4$),需分批提交避免OOM。 4. **性能权衡**: - 优点:完全控制线程池,避免`@Async`的AOP拦截限制(如private方法问题)[^3]。 - 缺点:需手动管理线程生命周期。 此方法适用于需精确控制并发和返回结果的场景,如批量数据处理、分布式任务协调等[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值