【业务功能篇 143】Spring容器中的ThreadPoolTaskExecutor多线程,LinkedBlockingQueue问题

博客围绕Spring展开,涉及ThreadPoolTaskExecutor多线程处理耗时数据接口,探讨其是否需手动关闭,还提及线程池在Spring中的使用与关闭,以及LinkedBlockingQueue引发的线上事故等信息技术相关内容。

 
 
package com.config;
 
import lombok.extern.slf4j.Slf4j;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import java.util.concurrent.ThreadPoolExecutor;
 
/**
 * 线程池配置
 *
 */
@Slf4j
@Configuration
@EnableAsync
public class ThreadConfigurer implements AsyncConfigurer {
 
 
    /**
     * taskExecutor
     * 
     * @return ThreadPoolTaskExecutor
     */
    @Primary
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 
        //获取电脑的处理器数量,一般电脑一个处理器有两个逻辑线程
        int num = Runtime.getRuntime().availableProcessors();
        // 核心线程数目 核心线程数最好设置为电脑的逻辑线程总数 + 1,达到最大化利用处理器
        executor.setCorePoolSize(num * 2 + 1);
        // 指定最大线程数
        executor.setMaxPoolSize(200);
        // 队列中最大的数目
        executor.setQueueCapacity(100);
        // 线程名称前缀
        executor.setThreadNamePrefix("taskExecutor-");
        // 拒绝策略:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 当调度器shutdown被调用时等待当前被调度的任务完成
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 线程空闲后的最大存活时间
        executor.setKeepAliveSeconds(60);
        // 线程池初始化
        executor.initialize();
        return executor;
    }
}

 

【业务功能篇42】ThreadPoolTaskExecutor多线程处理耗时较高的数据接口-优快云博客

 

Spring容器中的ThreadPoolTaskExecutor是否需要手动关闭_threadpoolexecutor需要手动关闭吗-优快云博客

多线程系列:二、线程池在 Spring 中的使用与关闭_springbatch split 线程池-优快云博客 

多线程系列:附、LinkedBlockingQueue 引发的一次线上事故_linkedblockingqueue thread乱了-优快云博客

<think>我们正在讨论如何在Spring框架中全局管理CompletableFuture的线程池。根据之前的对话,我们知道CompletableFuture默认使用ForkJoinPool.commonPool(),但在生产环境中,我们通常需要自定义线程池并全局管理。 在Spring中,我们可以通过以下几种方式全局管理CompletableFuture的线程池: 1. 使用@Bean定义全局线程池,然后在代码中注入并使用。 2. 通过实现AsyncConfigurer接口来自定义异步执行的线程池,这样@Async注解的方法和CompletableFuture都可以使用同一个线程池(需要额外配置)。 3. 使用第三方库(如Guava、Hutool)的线程池,但同样需要将其定义为Spring Bean以便全局管理。 但是,请注意:CompletableFuture本身并不直接使用Spring的@Async注解的线程池,除非我们显式地将自定义线程池传递给CompletableFuture的方法(如supplyAsync(Supplier, Executor))。因此,我们需要一个全局的线程池Bean,然后在代码中引用它。 步骤: 1. 定义一个线程池的Bean(例如,使用ThreadPoolTaskExecutor或ThreadPoolExecutor)。 2. 在需要使用CompletableFuture的地方,注入这个Bean,并作为参数传递给CompletableFuture的异步方法。 然而,如果我们希望所有CompletableFuture的异步操作都默认使用这个自定义线程池,而不是每次显式传入,目前Java本身并没有提供这样的全局设置。因此,我们需要自己封装一个工具类,或者通过AOP来拦截CompletableFuture的创建并传入自定义线程池。 但在Spring环境下,更常见的做法是: - 创建一个全局的Executor Bean。 - 提供一个工具类,该工具类使用这个Executor来创建CompletableFuture。 另外,如果我们使用@Async注解,我们可以通过AsyncConfigurer来设置默认的线程池,但是这个线程池仅用于@Async注解的方法,并不会影响CompletableFuture。因此,我们需要为CompletableFuture单独配置。 参考引用[3]中,CompletableFuture的异步方法可以接受一个Executor参数。因此,我们可以通过依赖注入的方式,在需要创建CompletableFuture的地方,使用我们自定义的线程池。 下面提供一种在Spring中全局管理CompletableFuture线程池的方案: 方案一:定义全局线程池Bean,并手动传入 步骤: 1. 配置线程池Bean。 2. 在业务代码中注入该Bean,并在创建CompletableFuture时使用。 方案二:封装工具类(推荐) 我们可以创建一个工具类,该工具类内部持有全局的Executor,并提供静态方法用于创建CompletableFuture。这样,我们就可以在代码中通过工具类来创建CompletableFuture,而无需每次都注入Executor。 但是,在Spring中,工具类如何获取Bean?我们可以使用依赖注入到静态变量,但这通常不推荐。另一种方法是使用@PostConstruct,或者让工具类实现ApplicationContextAware,但这样稍显复杂。 方案三:使用Spring的自动注入和组件 我们可以创建一个服务类,里面包含我们自定义的Executor,然后提供创建CompletableFuture的方法。这样,其他组件通过注入这个服务类来创建CompletableFuture,从而统一使用同一个线程池。 下面给出具体实现示例: 1. 定义全局线程池配置类(使用ThreadPoolTaskExecutor,它是Spring对ThreadPoolExecutor的封装): ```java @Configuration public class ThreadPoolConfig { @Bean("globalThreadPool") public Executor globalThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 配置线程池参数 executor.setCorePoolSize(10); executor.setMaxPoolSize(50); executor.setQueueCapacity(1000); executor.setThreadNamePrefix("global-thread-"); executor.initialize(); return executor; } } ``` 2. 创建CompletableFuture的工具类(可选方案二): ```java @Component public class CompletableFutureUtil { private static Executor executor; @Autowired public CompletableFutureUtil(@Qualifier("globalThreadPool") Executor executor) { CompletableFutureUtil.executor = executor; } public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return CompletableFuture.supplyAsync(supplier, executor); } public static CompletableFuture<Void> runAsync(Runnable runnable) { return CompletableFuture.runAsync(runnable, executor); } } ``` 注意:这种方式通过静态变量持有Executor,但是存在一个问题:当有多个线程池Bean时,我们只能注入一个。而且,如果静态变量被覆盖,可能会引起问题。另外,Spring不推荐直接静态注入,因为生命周期管理复杂。 3. 方案三:不使用静态工具类,而是通过服务类: ```java @Service public class AsyncService { @Autowired @Qualifier("globalThreadPool") private Executor executor; public <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return CompletableFuture.supplyAsync(supplier, executor); } public CompletableFuture<Void> runAsync(Runnable runnable) { return CompletableFuture.runAsync(runnable, executor); } } ``` 然后在其他组件中注入AsyncService,并使用它来创建CompletableFuture。这样,所有异步任务都通过同一个线程池执行。 另外,如果我们想让CompletableFuture默认使用我们自定义的线程池,我们可以通过AOP来拦截CompletableFuture的创建,但是这样比较复杂,且可能影响其他库的使用。因此,方案三(服务类)是更清晰、更安全的方式。 另外,参考引用[1]中提到了@Async注解和AsyncConfigurer接口,但这只影响@Async注解的方法。如果我们希望CompletableFuture也使用同一个线程池,我们可以在自定义的AsyncConfigurer中将我们配置的线程池返回,然后将这个线程池Bean注入到上述的AsyncService中。这样,整个应用就统一使用一个线程池了。 步骤: 1. 在AsyncConfigurer中返回自定义的线程池。 2. 将同样的线程池Bean注入到AsyncService中。 配置AsyncConfigurer: ```java @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Autowired @Qualifier("globalThreadPool") private Executor executor; @Override public Executor getAsyncExecutor() { return executor; // 这样@Async注解的方法也会使用这个线程池 } } ``` 这样,我们就实现了整个应用(包括@Async方法和CompletableFuture)都使用同一个全局线程池。 总结: 在Spring框架中全局管理CompletableFuture线程池,需要以下步骤: 1. 定义一个线程池Bean(如ThreadPoolTaskExecutor)。 2. 配置AsyncConfigurer,使得@Async注解的方法使用这个线程池。 3. 创建一个服务类(如AsyncService),注入这个线程池,并提供创建CompletableFuture的方法。 4. 在业务代码中,通过AsyncService来创建CompletableFuture,而不是直接使用CompletableFuture的静态方法。 这样,我们就实现了全局统一的线程池管理,便于监控和调优。 注意:线程池的配置参数(核心线程数、最大线程数、队列容量等)需要根据实际业务场景进行调整,并配合监控(如引用[5]中提到的监控方法)来确保线程池健康运行。 相关问题: 1. 如何监控Spring中全局线程池的运行状态? 2. 在Spring Boot中如何动态调整线程池参数? 3. 如何避免Spring异步任务和CompletableFuture任务之间的线程池竞争? 4. 如何为不同的业务场景配置多个线程池? </think>在 Spring 框架中全局管理 CompletableFuture 线程池,可通过以下三种核心方法实现: --- ### 一、定义全局线程池 Bean(推荐) 通过 `@Bean` 创建线程池并注入 Spring 容器,在业务代码中显式调用 ```java @Configuration public class ThreadPoolConfig { @Bean("globalThreadPool") public Executor globalThreadPool() { return new ThreadPoolExecutor( 10, // 核心线程数 50, // 最大线程数 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), // 有界队列防 OOM new CustomThreadFactory("global-pool-") // 自定义线程命名 ); } } // 业务层调用 @Service public class OrderService { @Autowired @Qualifier("globalThreadPool") private Executor executor; public CompletableFuture<Void> process() { return CompletableFuture.runAsync(() -> { // 业务逻辑 }, executor); // 显式指定线程池 } } ``` **优势**: ✅ 线程池参数完全可控(核心线程数/队列容量等) ✅ 支持多线程池隔离不同业务[^1] ✅ 结合监控工具实现运行时观测(如 Micrometer)[^5] --- ### 二、通过 `AsyncConfigurer` 接口统一配置 实现 `AsyncConfigurer` 接口定义默认异步执行器 ```java @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20); executor.setMaxPoolSize(100); executor.setThreadNamePrefix("async-global-"); executor.initialize(); return executor; } } // 使用示例(需配合@Async) @Async // 自动使用全局线程池 public CompletableFuture<String> fetchData() { return CompletableFuture.supplyAsync(() -> {...}); } ``` **注意**: ⚠️ 此配置仅对 `@Async` 注解生效 ⚠️ CompletableFuture 需通过 `supplyAsync()` 无参方法触发(底层默认使用 `AsyncConfigurer` 的线程池)[^2][^3] --- ### 三、自定义 `CompletableFuture` 工厂类 封装线程池注入逻辑,简化业务调用 ```java @Component public class AsyncFactory { private final Executor executor; @Autowired public AsyncFactory(@Qualifier("globalThreadPool") Executor executor) { this.executor = executor; } public <U> CompletableFuture<U> supply(Supplier<U> supplier) { return CompletableFuture.supplyAsync(supplier, executor); } } // 业务调用 @Service public class PaymentService { @Autowired private AsyncFactory asyncFactory; public void execute() { asyncFactory.supply(() -> {...}); // 自动使用全局线程池 } } ``` **优势**: ✅ 业务代码与线程池解耦 ✅ 统一异常处理入口 ✅ 支持动态切换线程池策略 --- ### 📊 方案对比 | 方案 | 适用场景 | 线程池隔离 | 代码入侵性 | |---------------------|---------------------------------|-----------|-----------| | `@Bean` + 显式调用 | 精确控制线程池使用的场景 | ✅ 支持多池 | 中等 | | `AsyncConfigurer` | 全应用统一线程池的简单场景 | ❌ 单池 | 低 | | 自定义工厂类 | 需要统一管控和扩展能力的复杂项目 | ✅ 支持多池 | 低 | --- ### 关键实践建议 1. **线程池参数动态化** 结合 Spring Cloud Config 或 Nacos 实现核心参数(如 `corePoolSize`)运行时调整[^5] 2. **监控集成** ```java // 暴露线程池指标 @Bean public ExecutorServiceMetrics executorMetrics( @Qualifier("globalThreadPool") ThreadPoolExecutor executor ) { return new ExecutorServiceMetrics( executor, "global_thread_pool", Tags.empty() ); } ``` 通过 Micrometer 对接 Prometheus+Grafana 监控队列堆积、拒绝任务数等指标[^5] 3. **异常处理** 全局定义 `CompletableFuture.exceptionally()` 避免任务失败导致线程阻塞: ```java public <T> CompletableFuture<T> withFallback(Supplier<T> supplier) { return CompletableFuture.supplyAsync(supplier, executor) .exceptionally(ex -> { log.error("Async task failed", ex); return fallbackValue; }); } ``` --- ### 🌰 动态调优示例 基于 Spring Boot Actuator 实时调整线程池: ```yaml # application.yml management: endpoint: threadpools: enabled: true ``` 通过 HTTP 请求动态扩容: ```bash curl -X POST http://host:port/actuator/thread-pools/globalThreadPool \ -H "Content-Type: application/json" \ -d '{"corePoolSize": 25, "maxPoolSize": 150}' ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值