Spring boot中的线程池-ThreadPoolTaskExecutor

本文介绍了Springboot工程中默认的ThreadPoolTaskExecutor及其配置,包括如何自定义线程池和阻塞队列,以及与JDK线程池的区别。特别提到了如何通过注解指定线程池执行异步任务。

一、jdk的阻塞队列:

在这里插入图片描述

二、Spring boot工程的有哪些阻塞队列呢?

1、默认注入的ThreadPoolTaskExecutor

视频解说:

线程池篇-springboot项目中的service层里简单注入ThreadPoolTaskExecutor并且使用_哔哩哔哩_bilibili

程序代码:ThreadPoolDemo/ThreadPool00 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com)

简单在service层注入的话是这样的:

@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;

同时在这里使用这个线程池:

@Override
public Object springbootThreadPool(Long count) {
    try {
        threadPoolTaskExecutor.execute(() -> {
            try {
                Thread.sleep(1000 * 1);
                log.debug("v me 50");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "nice";
}

以debug方式启动项目来查看一下,发现这里默认使用的阻塞队列是:

在这里插入图片描述

在这里插入图片描述

2、自定义ThreadPoolTaskExecutor

视频解说:

【2】https://www.bilibili.com/video/BV1Qu4y1X7zk

【3】https://www.bilibili.com/video/BV1Cu4y1i7Ae

程序代码:

https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/ThreadPoolDemo/ThreadPool0

验证方式1-通过启动springboot工程,通过debug形式查看:

@Bean("xinTaskExecutor")
public Executor xinTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    //设置线程池参数信息
    taskExecutor.setCorePoolSize(10);
    taskExecutor.setMaxPoolSize(50);
    taskExecutor.setQueueCapacity(0);
    taskExecutor.setKeepAliveSeconds(60);
    taskExecutor.setThreadNamePrefix("xinTaskExecutor--");
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    taskExecutor.setAwaitTerminationSeconds(60);

    //修改拒绝策略为使用当前线程执行
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
    //初始化线程池
    taskExecutor.initialize();
    return taskExecutor;
}

验证方式2-main方法创建并初始化:

通过debug形式查看

①查看initialize方法就可以了

public static void main(String[] args) {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    //设置线程池参数信息
    taskExecutor.setCorePoolSize(10);
    taskExecutor.setMaxPoolSize(50);
    taskExecutor.setQueueCapacity(0);
    taskExecutor.setKeepAliveSeconds(60);
    taskExecutor.setThreadNamePrefix("myExecutor--");
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    taskExecutor.setAwaitTerminationSeconds(10);

    //修改拒绝策略为使用当前线程执行
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
    //初始化线程池
    taskExecutor.initialize();
}

②可以看到ExecutorConfigurationSupport类里面有这个方法

public void initialize() {
   if (logger.isInfoEnabled()) {
      logger.info("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
   }
   if (!this.threadNamePrefixSet && this.beanName != null) {
      setThreadNamePrefix(this.beanName + "-");
   }
   this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler);
}

直接看this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler)

③来到org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

@Override
protected ExecutorService initializeExecutor(
      ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

   BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);

   ThreadPoolExecutor executor;
   if (this.taskDecorator != null) {
      executor = new ThreadPoolExecutor(
            this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
            queue, threadFactory, rejectedExecutionHandler) {
         @Override
         public void execute(Runnable command) {
            Runnable decorated = taskDecorator.decorate(command);
            if (decorated != command) {
               decoratedTaskMap.put(decorated, command);
            }
            super.execute(decorated);
         }
      };
   }
   else {
      executor = new ThreadPoolExecutor(
            this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
            queue, threadFactory, rejectedExecutionHandler);

   }

   if (this.allowCoreThreadTimeOut) {
      executor.allowCoreThreadTimeOut(true);
   }

   this.threadPoolExecutor = executor;
   return executor;
}

直接看createQueue(this.queueCapacity)即可

org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor#createQueue

可以看到要么new LinkedBlockingQueue<>(queueCapacity)要么就new SynchronousQueue<>()

protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
   if (queueCapacity > 0) {
      return new LinkedBlockingQueue<>(queueCapacity);
   }
   else {
      return new SynchronousQueue<>();
   }
}

那么有没有其他阻塞队列可选呢?这个我就没详细去看了,可以自己尝试下找一下有没有其他方式可以的,难道说重写在里面的方法吗?可行性有待验证。如果创建一个继承了org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor类,且重写了createQueue方法的话那么可以考虑下。就这样吧。

三、和jdk的线程池的区别

1、感觉没什么区别,因为ThreadPoolTaskExecutor内使用的线程池本来就是成员变量中的

@Nullable
private ThreadPoolExecutor threadPoolExecutor;

2、springboot的项目里可以通过注解方式来执行方法

只不过指定使用哪个线程池来执行要异步执行方法的内容。

https://www.bilibili.com/video/BV1A14y1B78x/

如果是默认的注解来执行内容则可能有其他问题:

https://www.bilibili.com/video/BV1Gu4y1q7TY

但是可以通过注解指定使用哪个线程池:

https://www.bilibili.com/video/BV1e44y1c7uE

<think>我们正在讨论的是在Spring Boot中配置ThreadPoolTaskExecutor以实现线程上下文传递的问题。根据引用内容,特别是引用[1]和引用[3],我们知道上下文传递的关键在于将主线程的上下文(如ThreadLocal变量)传递到子线程中。在ThreadPoolExecutor中,我们通常需要重写execute方法,在启动新线程时保存上下文,并在执行run方法时恢复。而ThreadPoolTaskExecutor作为Spring的封装,提供了更便捷的方式。 另外,引用[3]提到了TransmittableThreadLocal,它是阿里开发的用于解决线程池中上下文传递问题的工具,比InheritableThreadLocal更适用于线程池场景。 因此,在Spring Boot中配置ThreadPoolTaskExecutor的线程上下文传递,我们可以采用以下两种主要方法: 1. 使用Spring的TaskDecorator接口,在任务执行前后进行上下文的设置和清理。 2. 使用TransmittableThreadLocal(如果需要传递ThreadLocal变量)。 下面详细介绍这两种方法。 方法一:使用TaskDecorator SpringThreadPoolTaskExecutor支持设置一个TaskDecorator,它可以在任务执行前进行一些装饰操作(比如设置上下文),在任务执行后进行清理。 步骤: 1. 创建一个TaskDecorator的实现类,在decorate方法中实现上下文的传递和清理。 2. 将TaskDecorator设置到ThreadPoolTaskExecutor中。 示例代码: 假设我们需要传递SecurityContext(Spring Security的上下文)和MDC(日志上下文)。 首先,定义TaskDecorator: ```java import org.slf4j.MDC; import org.springframework.core.task.TaskDecorator; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; public class ContextCopyingTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { // 获取当前线程的上下文(安全上下文、MDC等) SecurityContext securityContext = SecurityContextHolder.getContext(); // 获取MDC上下文 Map<String, String> contextMap = MDC.getCopyOfContextMap(); return () -> { try { // 将主线程的上下文设置到当前线程(任务执行的线程) SecurityContextHolder.setContext(securityContext); if (contextMap != null) { MDC.setContextMap(contextMap); } runnable.run(); } finally { // 清理,防止内存泄漏 SecurityContextHolder.clearContext(); MDC.clear(); } }; } } ``` 然后,在配置ThreadPoolTaskExecutor时设置这个TaskDecorator: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class AsyncConfig { @Bean(name = "taskExecutor") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Async-Thread-"); // 设置TaskDecorator executor.setTaskDecorator(new ContextCopyingTaskDecorator()); return executor; } } ``` 这样,当使用这个线程池执行异步任务时,就会自动传递安全上下文和MDC日志上下文。 方法二:使用TransmittableThreadLocal 如果需要传递的是自定义的ThreadLocal变量,可以使用TransmittableThreadLocal。这种方法需要引入阿里巴巴的TransmittableThreadLocal库。 首先,添加Maven依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.14.2</version> </dependency> ``` 然后,将需要传递的ThreadLocal变量声明为TransmittableThreadLocal类型: ```java public class MyContextHolder { public static final TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); } ``` 但是,仅仅这样声明还不能自动在ThreadPoolTaskExecutor中传递。我们需要配合TtlExecutors来装饰线程池。但是ThreadPoolTaskExecutorSpring线程池,我们可以通过TaskDecorator来集成TransmittableThreadLocal。 我们可以创建一个TaskDecorator,利用TtlRunnable来装饰Runnable: ```java import com.alibaba.ttl.TtlRunnable; import org.springframework.core.task.TaskDecorator; public class TtlTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { // 使用TtlRunnable包装,这样就可以传递TransmittableThreadLocal return TtlRunnable.get(runnable); } } ``` 然后,将这个TaskDecorator设置到ThreadPoolTaskExecutor中: ```java @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // ... 配置其他参数 executor.setTaskDecorator(new TtlTaskDecorator()); return executor; } ``` 这样,所有通过该线程池执行的任务都会自动传递TransmittableThreadLocal的值。 注意:方法二需要额外引入依赖,且适用于需要传递自定义ThreadLocal变量的场景。如果只是传递Spring Security上下文或MDC,方法一足够。 总结: 在Spring Boot中配置ThreadPoolTaskExecutor的线程上下文传递,推荐使用TaskDecorator接口。根据具体需要传递的上下文内容,可以选择直接复制上下文(如方法一)或者集成TransmittableThreadLocal(如方法二)。 另外,引用[2]中提到了使用虚拟线程(Virtual Thread)的异步方法。在Spring Boot 3.2及以上版本,我们可以使用虚拟线程,但虚拟线程的上下文传递机制与原生的线程池有所不同。由于虚拟线程是轻量级的,且每个虚拟线程都有自己的上下文,因此我们可以通过设置系统属性来启用虚拟线程的上下文传递。但是,目前Spring Boot对虚拟线程的支持还比较新,具体上下文传递的配置可能有所不同。 这里我们主要讨论的是传统线程池的上下文传递。 最后,关于引用[1]中提到的重写ThreadPoolExecutor的execute方法,在ThreadPoolTaskExecutor中我们不需要这样做,因为ThreadPoolTaskExecutor已经提供了TaskDecorator这样的扩展点。 因此,在Spring Boot项目中,使用TaskDecorator是官方推荐的方式。 §§相关问题§§ 1. 如何在使用ThreadPoolTaskExecutor时传递Spring Security上下文? 2. TransmittableThreadLocal和InheritableThreadLocal有什么区别? 3. 在Spring Boot中如何为不同的异步任务配置不同的线程池? </think>### Spring Boot中配置`ThreadPoolTaskExecutor`线程上下文传递的方法 在Spring Boot中配置线程上下文传递,核心是通过`TaskDecorator`接口实现上下文复制和清理。以下是完整实现步骤: --- #### 一、基础配置(传递SecurityContext/MDC) ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.slf4j.MDC; @Configuration public class AsyncConfig { @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Async-"); // 关键:设置上下文复制装饰器 executor.setTaskDecorator(new ContextCopyingTaskDecorator()); return executor; } static class ContextCopyingTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { // 1. 捕获主线程上下文 SecurityContext securityContext = SecurityContextHolder.getContext(); Map<String, String> mdcContext = MDC.getCopyOfContextMap(); return () -> { try { // 2. 将上下文注入子线程 SecurityContextHolder.setContext(securityContext); if (mdcContext != null) MDC.setContextMap(mdcContext); // 3. 执行异步任务 runnable.run(); } finally { // 4. 清理防止内存泄漏 SecurityContextHolder.clearContext(); MDC.clear(); } }; } } } ``` --- #### 二、扩展配置(支持自定义ThreadLocal) 使用阿里`TransmittableThreadLocal`解决线程池上下文传递问题: 1. 添加依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.14.2</version> </dependency> ``` 2. 自定义上下文容器: ```java public class CustomContextHolder { public static final TransmittableThreadLocal<String> CONTEXT = new TransmittableThreadLocal<>(); } ``` 3. 增强TaskDecorator: ```java public class EnhancedTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { // 获取所有需传递的上下文 SecurityContext securityContext = SecurityContextHolder.getContext(); Map<String, String> mdcContext = MDC.getCopyOfContextMap(); String customContext = CustomContextHolder.CONTEXT.get(); return TtlRunnable.get(() -> { try { SecurityContextHolder.setContext(securityContext); if (mdcContext != null) MDC.setContextMap(mdcContext); CustomContextHolder.CONTEXT.set(customContext); // 注入自定义上下文 runnable.run(); } finally { SecurityContextHolder.clearContext(); MDC.clear(); CustomContextHolder.CONTEXT.remove(); } }); } } ``` --- #### 三、YAML参数化配置 ```yaml spring: task: execution: pool: core-size: 5 max-size: 20 queue-capacity: 100 thread-name-prefix: "AppTask-" ``` --- ### 关键机制说明 1. **TaskDecorator原理** 在提交任务到线程池队列前,通过装饰器模式捕获当前线程上下文: ```mermaid graph LR A[主线程提交任务] --> B[TaskDecorator捕获上下文] B --> C[包装Runnable对象] C --> D[存入线程池队列] D --> E[工作线程执行] E --> F[装饰器注入上下文] F --> G[执行业务逻辑] G --> H[清理上下文] ``` 2. **内存泄漏防护** 必须通过`try-finally`清理ThreadLocal: - `SecurityContextHolder.clearContext()` - `MDC.clear()` - `TransmittableThreadLocal.remove()` 3. **性能影响** 上下文复制操作的时间复杂度为$O(k)$(k为上下文变量数量),在典型业务场景中可忽略不计[^1]。 --- ### 验证方法 ```java @SpringBootTest public class ContextPropagationTest { @Autowired private ThreadPoolTaskExecutor taskExecutor; @Test void testSecurityContextPassing() { // 模拟登录用户 SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(new TestingAuthenticationToken("user", "pass")); SecurityContextHolder.setContext(context); taskExecutor.execute(() -> { // 验证子线程能获取用户信息 assertThat(SecurityContextHolder.getContext().getAuthentication().getName()) .isEqualTo("user"); }); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值