关注公众号:程序员老左 发现更多新技术
这篇文章是用来详细讲解 @Async 注解,它解决的问题,以及使用方法,并提供由易到难的实例,最后用大白话总结,方便各位理解。
一、@Async 是什么?解决了什么问题?
@Async 是什么: @Async 是 Spring 框架提供的一个注解,用于将方法标记为异步执行。简单来说,就是让这个方法在另一个线程中执行,而不是在调用它的线程中执行。
解决了什么问题:
提高响应速度: 当一个方法执行时间较长时,如果同步执行,会导致调用它的线程阻塞,影响程序的响应速度。使用 @Async 可以将这个方法放到另一个线程中执行,让调用线程可以继续执行其他任务,从而提高程序的响应速度。
提高并发能力: 当需要同时执行多个耗时任务时,可以使用 @Async 将这些任务放到不同的线程中执行,从而提高程序的并发能力。
避免阻塞主线程: 在 Web 应用中,如果某个请求处理需要执行耗时操作,可以使用 @Async 将这个操作放到另一个线程中执行,避免阻塞主线程,从而提高 Web 应用的吞吐量。
二、@Async 的使用方法
开启异步支持: 在 Spring Boot 应用的主类上添加 @EnableAsync 注解,开启异步支持。
标记异步方法: 在需要异步执行的方法上添加 @Async 注解。
三、@Async 实例 (由易到难)
1. 简单示例:异步发送邮件
需求: 用户注册成功后,异步发送一封欢迎邮件。
步骤:
创建 Spring Boot 应用:
使用 Spring Initializr 创建一个 Spring Boot 应用。
添加 spring-boot-starter-web 依赖。
开启异步支持:
在 MyApplication.java (或者你的主类) 中添加 @EnableAsync 注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // 开启异步支持
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
创建邮件发送服务:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Async // 标记为异步方法
public void sendWelcomeEmail(String email) {
// 模拟发送邮件,耗时 5 秒
System.out.println("Sending welcome email to " + email + "...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Welcome email sent to " + email);
}
}
解释:
@Async:将 sendWelcomeEmail() 方法标记为异步方法。
Thread.sleep(5000):模拟发送邮件的耗时操作。
创建用户注册服务:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private EmailService emailService;
public void registerUser(String email) {
// 模拟用户注册
System.out.println("Registering user with email: " + email);
// 异步发送欢迎邮件
emailService.sendWelcomeEmail(email);
System.out.println("User registered successfully.");
}
}
解释:
emailService.sendWelcomeEmail(email):调用 EmailService 的 sendWelcomeEmail() 方法发送邮件。
创建 Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/register")
public String register(@RequestParam("email") String email) {
userService.registerUser(email);
return "User registration initiated. Check console for async email sending.";
}
}
运行应用:
运行 MyApplication 类。
测试 API:
在浏览器或使用 curl 命令访问以下 URL:
bash 代码解读复制代码http://localhost:8080/register?email=test@example.com
你应该会看到 "User registration initiated." 的响应,并且在控制台中会看到异步发送邮件的日志。
// MyApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
// EmailService.java
package com.example;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Async
public void sendWelcomeEmail(String email) {
System.out.println("Sending welcome email to " + email + "...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Welcome email sent to " + email);
}
}
// UserService.java
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private EmailService emailService;
public void registerUser(String email) {
System.out.println("Registering user with email: " + email);
emailService.sendWelcomeEmail(email);
System.out.println("User registered successfully.");
}
}
// UserController.java
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/register")
public String register(@RequestParam("email") String email) {
userService.registerUser(email);
return "User registration initiated. Check console for async email sending.";
}
}
2. 中级示例:自定义线程池
需求: 使用自定义的线程池来执行异步任务。
步骤:
创建线程池配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor") // 指定线程池的名称
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(25); // 队列容量
executor.setThreadNamePrefix("MyAsyncThread-"); // 线程名称前缀
executor.initialize();
return executor;
}
}
解释:
@Configuration:将 AsyncConfig 类标记为一个配置类。
@EnableAsync:开启异步支持。
@Bean(name = "taskExecutor"):创建一个名为 taskExecutor 的 Bean,类型为 Executor。
ThreadPoolTaskExecutor:Spring 提供的线程池实现。
setCorePoolSize():设置核心线程数。
setMaxPoolSize():设置最大线程数。
setQueueCapacity():设置队列容量。
setThreadNamePrefix():设置线程名称前缀。
指定使用的线程池:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Async("taskExecutor") // 指定使用的线程池名称
public void sendWelcomeEmail(String email) {
System.out.println("Sending welcome email to " + email + "...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Welcome email sent to " + email);
}
}
解释:
@Async("taskExecutor"):指定使用名为 taskExecutor 的线程池来执行 sendWelcomeEmail() 方法。
3. 高级示例:异步任务的异常处理
需求: 捕获异步任务中发生的异常,并进行处理。
步骤:
实现 AsyncConfigurer 接口:
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
// 可以自定义线程池
return null;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncExceptionHandler();
}
}
解释:
AsyncConfigurer:Spring 提供的接口,用于配置异步任务。
getAsyncExecutor():可以自定义线程池。
getAsyncUncaughtExceptionHandler():用于指定异步任务的异常处理器。
创建异常处理器:
java 代码解读复制代码
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import java.lang.reflect.Method;
public class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
System.err.println("Async method has uncaught exception:");
System.err.println("Method: " + method.getName());
System.err.println("Exception message: " + ex.getMessage());
}
}
解释:
AsyncUncaughtExceptionHandler:Spring 提供的接口,用于处理异步任务中发生的未捕获异常。
handleUncaughtException():处理异常的方法。
四、大白话总结
@Async: 就像请了一个 "临时工",帮你干一些比较慢的活,这样你就不用一直等着,可以先去干其他的事情。
@EnableAsync: 告诉 Spring "我要用临时工了!"
线程池: 就像一个 "临时工管理中心",可以管理多个 "临时工",让他们更有效率地工作。
异常处理: 就像给 "临时工" 买了保险,万一他们出了什么问题,也能及时处理。
更简洁的总结:
@Async 让方法异步执行,就像请了个临时工。
@EnableAsync 开启异步支持,告诉 Spring 可以用临时工了。
可以自定义线程池,管理临时工。
可以进行异常处理,给临时工买保险。
希望这篇文章能够帮助你理解 @Async 注解!
作者:流_云
链接:https://juejin.cn