# 使用 `@Async` 实现线程池 ```mermaid 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` 注解来实现。 ```java @Configuration @EnableAsync public class AppConfig { } ``` 或者在 Spring Boot 主类上添加: ```java @SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` --- ## 二、配置线程池(推荐) Spring 提供了多种方式来配置异步任务执行的线程池,推荐使用 `TaskExecutor` 来自定义线程池,以控制并发行为。 ### 1. 使用 `@Bean` 配置自定义线程池 ```java @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")` 指定使用自定义的线程池。 ```java @Service public class MyService { @Async("customTaskExecutor") public void asyncMethod() { // 异步执行的操作 } } ``` --- ## 三、使用默认线程池 如果未配置自定义线程池,Spring 会使用默认的 `SimpleAsyncTaskExecutor`,但这个线程池不推荐用于生产环境,因为它**每次都会创建新线程**,容易引发资源耗尽问题。 可以使用 `@Async` 并不指定线程池名称,Spring 会自动使用默认的线程池(需确保 `@EnableAsync` 已开启): ```java @Service public class MyService { @Async public void defaultAsyncMethod() { // 默认线程池执行 } } ``` --- ## 四、异步方法调用注意事项 - `@Async` 注解的方法不能和调用它的方法在同一个类中,否则不会生效。因为 Spring 的 AOP 机制需要通过代理来实现异步调用,若在同一个类中调用,代理无法生效。 - 异步方法不能返回 `void`,否则无法捕获异常。**建议返回 `Future` 或 `CompletableFuture`**。 - 异步方法的异常处理:如果希望捕获异步方法中的异常,可以在方法中使用 `try-catch`,或者通过 `Future` 返回异常信息。 - 如果异步方法中涉及事务控制,需注意事务的传播机制。 --- ## 五、示例代码 ### 示例 1:简单异步方法 ```java @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 ```java @Service public class AsyncService { @Async("customTaskExecutor") public Future asyncTask(String input) { try { Thread.sleep(2000); return new AsyncResult<>("Processed: " + input); } catch (InterruptedException e) { return new AsyncResult<>("Error occurred"); } } } ``` 在调用处使用: ```java @Autowired private AsyncService asyncService; public void callAsyncTask() { Future 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` ```java @Async public Future asyncMethod() { try { // … return new AsyncResult<>("Success"); } catch (Exception e) { return new AsyncResult<>("Error: " + e.getMessage()); } } ``` ### 使用 `CompletableFuture` ```java @Async public CompletableFuture 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` 注解实现定时异步任务