spring async 入门到精通

一、为什么需要异步线程池

在 Java 里,异步执行本质就是把任务提交给线程池执行,而不是阻塞在当前线程。

  • 场景:IO 请求(HTTP、RPC、DB)、消息消费、批量计算。

  • 目标:减少主线程阻塞、提升吞吐量、平滑限流。

核心类就是 ThreadPoolExecutor 最佳实践-优快云博客 中介绍的 ExecutorExecutorServiceThreadPoolExecutor。

Spring 在此之上封装,减少配置复杂度 + 统一上下文传播。


二、Spring 异步线程池入门

1. 开启异步支持

@SpringBootApplication
@EnableAsync  // 开启异步支持
public class DemoApplication {}

2. 定义异步线程池

Spring 推荐使用 ThreadPoolTaskExecutor(包装了 JDK 的 ThreadPoolExecutor,额外支持 Spring 生命周期管理、监控指标)。

@Configuration
public class AsyncConfig {

    @Bean("asyncExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);          // 核心线程数
        executor.setMaxPoolSize(10);          // 最大线程数
        executor.setQueueCapacity(100);       // 队列容量
        executor.setKeepAliveSeconds(60);     // 空闲线程存活时间
        executor.setThreadNamePrefix("async-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

3. 使用异步方法

@Service
public class MyService {

    @Async("asyncExecutor")   // 指定线程池
    public CompletableFuture<String> doWork() throws InterruptedException {
        Thread.sleep(2000);
        return CompletableFuture.completedFuture("done");
    }
}

4. 异步返回值类型

  • void → 仅执行,不关心结果

  • Future<T> → JDK 传统异步返回

  • CompletableFuture<T> → 支持链式编排,更现代(推荐)


四、源码分析

1. @EnableAsync 

  • 入口:@EnableAsync

  • 导入类:AsyncConfigurationSelector

  • 它会注册:

    • AsyncAnnotationBeanPostProcessor → 核心,扫描 @Async 方法

    • AsyncExecutionInterceptor → 方法调用拦截器

2. Bean 后处理:AsyncAnnotationBeanPostProcessor

  • 继承 AbstractBeanFactoryAwareAdvisingPostProcessor

  • 作用:扫描带 @Async 注解的方法,给对应的 bean 生成代理(JDK 动态代理或 CGLIB)

  • 代理会织入一个 Advisor,指向 AsyncExecutionInterceptor

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
        implements BeanFactoryAware, BeanClassLoaderAware, Ordered {
    // 核心逻辑在 postProcessAfterInitialization()
    // 为带 @Async 的 bean 生成代理
}

3. 拦截器:AsyncExecutionInterceptor

  • 继承 AsyncExecutionAspectSupport

  • 核心方法 invoke(…)

    1. 拿到原始方法的 MethodInvocation

    2. 封装成一个 CallableRunnable

    3. 提交给 Executor(默认 SimpleAsyncTaskExecutor,可通过配置改为自定义线程池)

    4. 处理返回值:如果返回 Future/CompletableFuture,会自动封装;否则返回空

关键片段(简化):

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
    Callable<Object> task = () -> invocation.proceed();
    Future<Object> result = this.executor.submit(task);
    return adaptToReturnType(result, invocation.getMethod());
}

4. 默认 Executor 的选择

  • 如果用户没配线程池,Spring 会用:

    • SimpleAsyncTaskExecutor(每次新建线程,没有复用)

  • 如果实现了 AsyncConfigurer,则用你提供的 Executor

  • 如果用 @Async("beanName"),会从容器里找对应的线程池 bean


五、最佳实践

1. 线程池定制(推荐)

@Bean("asyncExecutor")
public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(50);
    executor.setQueueCapacity(200);
    executor.setKeepAliveSeconds(60);
    executor.setThreadNamePrefix("async-");
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
}

2. 使用 CompletableFuture

Future 更现代,支持链式编排、组合任务。

@Async("asyncExecutor")
public CompletableFuture<String> asyncJob() {
    return CompletableFuture.completedFuture("OK");
}

3. 异常处理

全局处理未捕获的异常:

@Configuration
public class AsyncExceptionConfig implements AsyncConfigurer {
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> log.error("Async error", ex);
    }
}

4. 上下文传递(日志 TraceId / 用户信息)

默认不会传递 ThreadLocal,需要 TaskDecorator:

executor.setTaskDecorator(r -> {
    Map<String, String> context = MDC.getCopyOfContextMap();
    return () -> {
        MDC.setContextMap(context);
        try { r.run(); } finally { MDC.clear(); }
    };
});

复杂情况可以用阿里巴巴的 TransmittableThreadLocal(TTL)

5. 优雅停机

应用关停时等待异步任务完成:

executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(30);

6. 多线程池隔离

不同业务独立线程池,避免“日志任务堵死业务任务”这种悲剧。


六、常见误区

  1. 调用自己类的 @Async 方法无效
    因为代理没生效,自调用绕过了 AOP。

  2. 返回值不是 Future/CompletableFuture
    如果是 void,异常不会抛到调用方,只能通过 AsyncUncaughtExceptionHandler 捕获。

  3. 上下文丢失
    日志 traceId、SecurityContext 这种 ThreadLocal 不会自动传递。

  4. 滥用异步
    异步适合 IO 密集,CPU 密集不建议大量开线程,容易竞争 CPU。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fire-flyer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值