2025-11-10 18:38:05 +08:00
2025-11-10 18:38:05 +08:00
2025-11-10 13:12:18 +08:00
2025-11-10 13:03:10 +08:00
2025-11-10 13:30:45 +08:00
2025-11-10 18:21:30 +08:00

使用 @Async 实现线程池

graph TD
    A[调用者调用异步方法] --> B[Spring AOP 代理拦截方法调用]
    B --> C{方法是否为 @Async?}
    C -->|是| D[封装为 FutureTask 或 Runnable]
    C -->|否| E[按正常方式执行]
    D --> F[提交到线程池 TaskExecutor]
    F --> G[线程池分配一个线程]
    G --> H[JVM 创建线程并执行任务]
    H --> I[任务执行完毕,返回结果]
    I --> J[调用者通过 Future 获取结果]

前置知识

@Async

@Async 是 Spring 框架提供的一个注解,用于支持异步方法调用。它允许我们标记一个方法为异步方法,这样该方法将在单独的线程中执行,而不是阻塞当前线程。

优点:

  • 提高应用程序的响应速度
  • 避免长时间运行的操作阻塞主线程
  • 适用于处理耗时操作、并行任务、异步通知等场景

使用场景:

  • 异步发送邮件、短信
  • 异步日志记录
  • 异步计算任务
  • 异步调用第三方服务

一、开启异步支持

要使用 @Async 注解,必须在 Spring 应用中开启异步支持。可以通过在主类或配置类上添加 @EnableAsync 注解来实现。

@Configuration
@EnableAsync
public class AppConfig {
}

或者在 Spring Boot 主类上添加:

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

二、配置线程池(推荐)

Spring 提供了多种方式来配置异步任务执行的线程池,推荐使用 TaskExecutor 来自定义线程池,以控制并发行为。

1. 使用 @Bean 配置自定义线程池

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "customTaskExecutor")
    public Executor customTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Custom-Async-");
        executor.initialize();
        return executor;
    }
}

2. 指定使用哪个线程池

可以通过 @Async("customTaskExecutor") 指定使用自定义的线程池。

@Service
public class MyService {

    @Async("customTaskExecutor")
    public void asyncMethod() {
        // 异步执行的操作
    }
}

三、使用默认线程池

如果未配置自定义线程池,Spring 会使用默认的 SimpleAsyncTaskExecutor,但这个线程池不推荐用于生产环境,因为它每次都会创建新线程,容易引发资源耗尽问题。

可以使用 @Async 并不指定线程池名称,Spring 会自动使用默认的线程池(需确保 @EnableAsync 已开启):

@Service
public class MyService {

    @Async
    public void defaultAsyncMethod() {
        // 默认线程池执行
    }
}

四、异步方法调用注意事项

  • @Async 注解的方法不能和调用它的方法在同一个类中,否则不会生效。因为 Spring 的 AOP 机制需要通过代理来实现异步调用,若在同一个类中调用,代理无法生效。
  • 异步方法不能返回 void,否则无法捕获异常。建议返回 Future<T> 或 CompletableFuture<T>。
  • 异步方法的异常处理:如果希望捕获异步方法中的异常,可以在方法中使用 try-catch,或者通过 Future 返回异常信息。
  • 如果异步方法中涉及事务控制,需注意事务的传播机制。

五、示例代码

示例 1:简单异步方法

@Service
public class AsyncService {

    @Async
    public void sendNotification(String message) {
        System.out.println("Sending notification: " + message);
        // 模拟耗时操作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

示例 2:使用自定义线程池并返回 Future

@Service
public class AsyncService {

    @Async("customTaskExecutor")
    public Future<String> asyncTask(String input) {
        try {
            Thread.sleep(2000);
            return new AsyncResult<>("Processed: " + input);
        } catch (InterruptedException e) {
            return new AsyncResult<>("Error occurred");
        }
    }
}

在调用处使用:

@Autowired
private AsyncService asyncService;

public void callAsyncTask() {
    Future<String> result = asyncService.asyncTask("test");
    try {
        System.out.println(result.get());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

六、线程池配置属性说明

属性名 描述
corePoolSize 核心线程数(常驻线程数量)
maxPoolSize 最大线程数(允许创建的最大线程数)
queueCapacity 任务队列容量(缓存未被立即执行的任务)
keepAliveSeconds 非核心线程的空闲时间(超过后会被销毁)
threadNamePrefix 线程名称前缀(用于调试和日志区分)
allowCoreThreadTimeOut 是否允许核心线程超时(默认 false)

七、异步方法异常处理

由于异步方法在独立线程中执行,不能直接抛出异常。可使用 Future 或 CompletableFuture 来捕获和处理异常。

使用 Future

@Async
public Future<String> asyncMethod() {
    try {
        // …
        return new AsyncResult<>("Success");
    } catch (Exception e) {
        return new AsyncResult<>("Error: " + e.getMessage());
    }
}

使用 CompletableFuture

@Async
public CompletableFuture<String> asyncMethod() {
    return CompletableFuture.supplyAsync(() -> {
        try {
            // …
            return "Success";
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    });
}

八、常见问题与排查

问题 解决方案
@Async 方法未执行 检查是否开启 @EnableAsync,是否在不同类中调用
线程池配置无效 确保 @Bean 配置正确,且 @Async 指定了正确的 bean 名称
线程池资源耗尽 调整线程池大小和队列容量,增加 queueCapacity
异常未被捕获 使用 Future 或 CompletableFuture 返回结果,避免 void 返回

九、相关类与接口

  • org.springframework.scheduling.annotation.Async
  • org.springframework.scheduling.annotation.EnableAsync
  • org.springframework.core.task.AsyncTaskExecutor
  • org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
  • org.springframework.util.concurrent.AsyncTask

十、最佳实践

  • 为不同业务场景配置不同的线程池
  • 使用 CompletableFuture 来处理异步任务中的返回结果和异常
  • 避免在异步方法中使用共享变量,防止线程安全问题
  • 控制线程池的大小和任务队列,防止资源浪费或阻塞
  • 结合 @Scheduled 注解实现定时异步任务
S
Description
📚 适用于 SpringBoot 的多种线程池实现
Readme MIT 51 KiB
Languages
Java 100%