springboot公共线程池配置

文章介绍了如何在SpringBoot应用中配置一个通用的线程池,包括核心线程数、最大线程数、队列容量和线程活跃时间。同时,还展示了如何通过UserTaskDecorator传递用户信息到线程中。
@Configuration
public class TzCommonThreadPoolConfig {

    /**
     * 线程池配置:核心线程数
     */
    @Value("${sys.pool.corePoolSize:8}")
    private int corePoolSize;

    /**
     * 线程池配置:最大线程数
     */
    @Value("${sys.pool.maxPoolSize:16}")
    private int maxPoolSize;

    /**
     * 线程池配置:队列容量
     */
    @Value("${sys.pool.commonQueueCapacity:2048}")
    private int queueCapacity;

    /**
     * 线程池配置:线程活跃时间(秒)
     */
    @Value("${sys.pool.commonKeepAliveSeconds:60}")
    private int keepAliveSeconds;

    /**
     * 通用公共线程池
     * <p>
     * 可传递当前用户信息
     * 非强业务性配置,默认使用通用配置量,队列容量和活跃时间自定义
     * 频率较高及业务性较强的建议自定义线程池处理
     *
     * @return Executor
     */
    @Bean(name = "commonThreadPool")
    public Executor commonThreadPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        // 设置队列容量
        executor.setQueueCapacity(queueCapacity);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置默认线程名称
        executor.setThreadNamePrefix("commonThreadPool-%d");
        // 增加 TaskDecorator 属性的配置
        executor.setTaskDecorator(new UserTaskDecorator());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化线程池
        executor.initialize();
        return executor;
    }
}
/**
* 用户信息传递装饰器
*/
public class UserTaskDecorator implements TaskDecorator {

    /**
     * Decorate the given {@code Runnable}, returning a potentially wrapped
     * {@code Runnable} for actual execution, internally delegating to the
     * original {@link Runnable#run()} implementation.
     *
     * @param runnable the original {@code Runnable}
     * @return the decorated {@code Runnable}
     */
    @Override
    public Runnable decorate(@NotNull Runnable runnable) {
        TzmallUser user = UserInfoContext.getUser();
        return () -> UserInfoContext.resetUserInfo(user, runnable::run);
    }
}
public class UserInfoContext {

    /**
     * 用户信息ThreadLocal
     */
    private static ThreadLocal<TzmallUser> userInfo = new ThreadLocal<>();

    /**
     * 获取Authentication
     */
    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 默认用户
     */
    private final static TzmallUser defaultUser;

    static {
        defaultUser = new TzmallUser(TzmallUser.SYSTEM, StrUtil.EMPTY, Collections.emptySet());
        defaultUser.setId(xxL);
        defaultUser.setName("系统");
        defaultUser.setFirmId(xxL);
        defaultUser.setFirmName("平台");
        defaultUser.setUserType(xx);
        defaultUser.setPhone("");
    }

    public UserInfoContext() {
    }

    /**
     * 获取用户信息
     */
    public static TzmallUser getUser(){
        // 如果本地缓存存在,则返回本地缓存TzUser
        if (ObjectUtil.isNotNull(userInfo.get())) {
            return userInfo.get();
        }
        Authentication authentication = getAuthentication();
        if (authentication == null) {
            return null;
        }
        return getUser(authentication);
    }


    /**
     * 获取用户信息
     *
     * @param authentication authentication
     * @return TzmallUser
     */
    public static TzmallUser getUser(Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if (principal instanceof TzmallUser) {
            return (TzmallUser) principal;
        }
        return null;
    }

    /**
     * 获取登录用户(如果当前未登录抛出异常!)
     *
     * @return TzmallUser
     */
    public static TzmallUser getLoginUser() {
        TzmallUser tzUser = getUser();
        if (tzUser == null) {
            throw new ServiceException(CodeMsg.LOGIN_INVALID);
        }
        return tzUser;
    }


    /**
     * 获取用户信息,如不存在取默认用户对象
     *
     */
    public static TzmallUser getUserOrDefault(){
        TzmallUser user = getUser();
        if (user == null) {
            return defaultUser;
        }
        return user;
    }

    /**
     * 重置登录信息,并执行代码
     *
     */
    public static <T> T resetUserInfo(TzmallUser tzUser, Supplier<T> supplier) {
        TzmallUser originalUser = userInfo.get();
        try {
            userInfo.set(tzUser);
            return supplier.get();
        } finally {
            if (originalUser == null) {
                userInfo.remove();
            } else {
                userInfo.set(originalUser);
            }
        }
    }

    /**
     * 重置登录信息,并执行代码
     *
     */
    public static void resetUserInfo(TzmallUser tzUser, LambdaExecutor lambdaExecutor) {
        TzmallUser originalUser = userInfo.get();
        try {
            userInfo.set(tzUser);
            lambdaExecutor.executor();
        } finally {
            if (originalUser == null) {
                userInfo.remove();
            } else {
                userInfo.set(originalUser);
            }
        }
    }
}

配置类放在common,引用该common的模块相当于建立了自己的线程池

#启动类添加
@EnableAsync
#使用
@Async("commonThreadPool")
### SpringBoot 中自定义线程池 `@Async` 配置无效的原因分析 在 Spring Boot 应用程序中,如果发现自定义线程池的异步功能未生效,通常是由以下几个原因引起的: #### 1. **默认线程池与自定义线程池冲突** Spring Boot 提供了一个默认的线程池配置,其 Bean 名称为 `applicationTaskExecutor` 并有一个别名 `TaskExecutor`。当开发者尝试引入自定义线程池时,如果没有正确命名或存在别名冲突,则可能导致 Spring 容器优先加载默认线程池而非自定义线程池[^3]。 解决方法是确保自定义线程池的 Bean 名称唯一,并避免与其默认别名重叠。可以通过以下方式实现: ```java @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "customThreadPoolTaskExecutor") // 使用唯一的名称 public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } } ``` --- #### 2. **缺少 `@EnableAsync` 注解** 为了使 `@Async` 注解生效,必须在应用程序的主要配置类上启用异步支持。这通常是通过添加 `@EnableAsync` 实现的。如果该注解缺失,即使配置了自定义线程池,也不会触发异步执行逻辑[^4]。 示例代码如下: ```java @SpringBootApplication @EnableAsync // 启用异步支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` --- #### 3. **静态方法 (`static`) 或者非公共方法 (`non-public methods`) 调用** 如果目标方法被声明为 `static` 或者是非公共访问权限的方法(如 `private` 方法),则无法通过 Spring 的动态代理机制拦截这些方法调用,从而导致 `@Async` 失效[^2]。 以下是修正后的代码片段: ```java @Service public class MyService { @Async("customThreadPoolTaskExecutor") // 显式指定使用的线程池 public void asyncMethod() { // 不应使用 static 和 private 关键字 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Async method executed."); } } ``` --- #### 4. **内部方法调用问题** 当在一个对象的内部直接调用带有 `@Async` 注解的方法时,由于 Java 反射机制的作用范围仅限于外部调用,因此这种情况下也无法激活异步行为[^4]。 例如,在下面的例子中,尽管 `asyncMethod()` 带有 `@Async` 注解,但由于它是从同一对象实例中调用的,仍然会同步运行。 ```java @Service public class MyService { @Async("customThreadPoolTaskExecutor") public void asyncMethod() { System.out.println("Executing asynchronously..."); } public void anotherMethod() { this.asyncMethod(); // 此处不会触发异步效果 } } ``` 要修复此问题,请始终通过 Spring 上下文中获取的服务接口来调用带 `@Async` 注解的方法。 --- #### 5. **返回值类型不符合要求** `@Async` 注解的目标方法可以返回两种类型的值:`void` 或者 `Future<?>` 类型及其子类(如 `CompletableFuture<?>`)。其他任何返回值都会导致框架忽略异步处理逻辑[^4]。 推荐做法如下所示: ```java @Service public class MyService { @Async("customThreadPoolTaskExecutor") public CompletableFuture<String> asyncWithResult() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return CompletableFuture.completedFuture("Async result"); } } ``` --- #### 6. **事务管理与异步并发冲突** 在同一方法中标记了 `@Transactional` 和 `@Async` 注解可能会引发不可预测的行为,因为两者都依赖于代理模式工作,而它们之间的交互可能破坏预期的功能[^4]。 建议将事务管理和异步操作分离到不同的服务层中分别处理。 --- ### 总结 综上所述,针对 Spring Boot 自定义线程池中的 `@Async` 配置失效问题,可以从以下几个方面逐一排查并解决问题: - 确认是否启用了 `@EnableAsync`; - 检查是否存在线程池名称/别名冲突; - 修改方法签名以移除 `static` 或调整为公有可见性; - 避免内部方法调用带来的局限性; - 设置合适的返回值类型; - 将事务控制独立出来单独设计。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值