副标题:异步编程的正确姿势,性能提升的秘密武器!🎯
🎬 开场:同步 vs 异步
问题场景
场景:用户注册
同步执行(耗时长):
register() {
1. 保存用户信息 → 100ms
2. 发送注册邮件 → 2000ms ⏱️
3. 发送短信通知 → 1000ms ⏱️
4. 记录操作日志 → 50ms
总耗时:3150ms ❌ 用户等待3秒多
}
异步执行(快速响应):
register() {
1. 保存用户信息 → 100ms
2. 发送注册邮件 → 异步执行 🚀
3. 发送短信通知 → 异步执行 🚀
4. 记录操作日志 → 异步执行 🚀
总耗时:100ms ✅ 用户只等待100ms
}
性能提升:31.5倍!
📚 @Async基本使用
开启异步支持
/**
* 1. 启用异步
*/
@Configuration
@EnableAsync // 开启异步支持
public class AsyncConfig {
}
/**
* 2. 定义异步方法
*/
@Service
public class EmailService {
/**
* 异步发送邮件
*/
@Async // 标记为异步方法
public void sendEmail(String to, String subject, String content) {
System.out.println("开始发送邮件,线程:" + Thread.currentThread().getName());
// 模拟发送邮件(耗时2秒)
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("邮件发送完成");
}
}
/**
* 3. 调用异步方法
*/
@Service
public class UserService {
@Autowired
private EmailService emailService;
public void register(User user) {
System.out.println("保存用户,线程:" + Thread.currentThread().getName());
// 保存用户
userMapper.insert(user);
// 异步发送邮件(不会阻塞)
emailService.sendEmail(user.getEmail(), "欢迎注册", "欢迎您!");
System.out.println("注册完成");
}
}
/**
* 输出:
* 保存用户,线程:http-nio-8080-exec-1
* 注册完成 ← 立即返回
* 开始发送邮件,线程:task-1 ← 异步执行
* 邮件发送完成
*/
🎯 异步方法的返回值
void(无返回值)
/**
* 无返回值的异步方法
*/
@Service
public class NotificationService {
@Async
public void sendNotification(String message) {
// 发送通知
System.out.println("发送通知:" + message);
}
}
// 调用
notificationService.sendNotification("hello"); // 不关心结果
Future(有返回值)
/**
* 返回Future的异步方法
*/
@Service
public class CalculateService {
@Async
public Future<Integer> calculate(int a, int b) {
System.out.println("开始计算,线程:" + Thread.currentThread().getName());
// 模拟耗时计算
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int result = a + b;
System.out.println("计算完成:" + result);
// 返回AsyncResult
return new AsyncResult<>(result);
}
}
// 调用
@Test
public void testAsync() throws Exception {
Future<Integer> future = calculateService.calculate(10, 20);
System.out.println("继续做其他事情...");
// 获取结果(阻塞)
Integer result = future.get(); // 等待异步方法执行完成
System.out.println("结果:" + result);
}
/**
* 输出:
* 开始计算,线程:task-1
* 继续做其他事情...
* 计算完成:30
* 结果:30
*/
CompletableFuture(推荐)
/**
* 返回CompletableFuture的异步方法
*/
@Service
public class UserService {
@Async
public CompletableFuture<User> getUserById(Long id) {
System.out.println("查询用户,线程:" + Thread.currentThread().getName());
// 模拟查询
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User(id, "张三");
// 返回CompletableFuture
return CompletableFuture.completedFuture(user);
}
}
// 调用
@Test
public void testCompletableFuture() {
CompletableFuture<User> future = userService.getUserById(1L);
// 链式调用
future.thenAccept(user -> {
System.out.println("用户名:" + user.getName());
}).exceptionally(ex -> {
System.out.println("异常:" + ex.getMessage());
return null;
});
System.out.println("继续做其他事情...");
}
🔧 配置线程池
自定义线程池(推荐)
/**
* 配置异步线程池
*/
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
/**
* 配置线程池
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(20);
// 队列容量
executor.setQueueCapacity(100);
// 线程名前缀
executor.setThreadNamePrefix("async-");
// 拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待任务完成后再关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
/**
* 异常处理器
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
System.out.println("异步方法异常:");
System.out.println("方法:" + method.getName());
System.out.println("参数:" + Arrays.toString(params));
System.out.println("异常:" + ex.getMessage());
}
};
}
}
多个线程池
/**
* 配置多个线程池
*/
@Configuration
@EnableAsync
public class AsyncConfig {
/**
* 邮件线程池
*/
@Bean("emailExecutor")
public Executor emailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("email-");
executor.initialize();
return executor;
}
/**
* 短信线程池
*/
@Bean("smsExecutor")
public Executor smsExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("sms-");
executor.initialize();
return executor;
}
}
/**
* 使用指定的线程池
*/
@Service
public class NotificationService {
@Async("emailExecutor") // 指定线程池
public void sendEmail(String to, String content) {
System.out.println("发送邮件,线程:" + Thread.currentThread().getName());
// 发送邮件
}
@Async("smsExecutor") // 指定线程池
public void sendSms(String phone, String content) {
System.out.println("发送短信,线程:" + Thread.currentThread().getName());
// 发送短信
}
}
🚨 常见陷阱
陷阱1:自调用失效 ❌
/**
* 问题:自调用@Async不生效
*/
@Service
public class UserService {
public void register(User user) {
// 保存用户
userMapper.insert(user);
// 自调用异步方法
this.sendEmail(user.getEmail()); // ❌ 不会异步执行!
}
@Async
public void sendEmail(String email) {
System.out.println("发送邮件,线程:" + Thread.currentThread().getName());
// 发送邮件
}
}
/**
* 原因:
* @Async基于AOP代理实现
* 自调用不经过代理,所以不会异步执行
*/
/**
* 解决方案1:注入自己
*/
@Service
public class UserService {
@Autowired
private UserService userService; // 注入自己的代理
public void register(User user) {
userMapper.insert(user);
// 通过代理调用
userService.sendEmail(user.getEmail()); // ✅ 异步执行
}
@Async
public void sendEmail(String email) {
System.out.println("发送邮件,线程:" + Thread.currentThread().getName());
}
}
/**
* 解决方案2:拆分成两个类
*/
@Service
public class UserService {
@Autowired
private EmailService emailService;
public void register(User user) {
userMapper.insert(user);
// 调用另一个Service
emailService.sendEmail(user.getEmail()); // ✅ 异步执行
}
}
@Service
public class EmailService {
@Async
public void sendEmail(String email) {
System.out.println("发送邮件,线程:" + Thread.currentThread().getName());
}
}
陷阱2:事务失效 ❌
/**
* 问题:@Async + @Transactional可能导致事务失效
*/
@Service
public class OrderService {
@Async
@Transactional // 事务可能失效
public void createOrder(Order order) {
// 插入订单
orderMapper.insert(order);
// 抛出异常
if (true) {
throw new RuntimeException("创建订单失败");
}
// 期望:订单回滚
// 实际:订单可能已经提交 ❌
}
}
/**
* 原因:
* @Async方法在新线程中执行
* 新线程没有事务上下文
*
* 解决方案:分开使用
*/
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// 在事务中插入订单
orderMapper.insert(order);
if (error) {
throw new RuntimeException("失败"); // 事务回滚 ✅
}
}
@Async
public void sendNotification(Long orderId) {
// 异步发送通知(不需要事务)
notificationService.send(orderId);
}
}
陷阱3:返回值被忽略 ❌
/**
* 问题:异步方法的返回值被忽略
*/
@Service
public class UserService {
@Async
public String getUsername(Long id) {
System.out.println("查询用户名");
return "张三";
}
}
// 调用
String username = userService.getUsername(1L);
System.out.println(username); // ❌ null!
/**
* 原因:
* @Async方法在新线程中执行
* 主线程立即返回null
*
* 解决方案:使用Future或CompletableFuture
*/
@Service
public class UserService {
@Async
public CompletableFuture<String> getUsername(Long id) {
System.out.println("查询用户名");
return CompletableFuture.completedFuture("张三");
}
}
// 调用
CompletableFuture<String> future = userService.getUsername(1L);
String username = future.get(); // ✅ 等待结果
System.out.println(username); // 张三
陷阱4:线程池未配置 ❌
/**
* 问题:没有配置线程池
*
* 默认使用SimpleAsyncTaskExecutor
* - 每次创建新线程
* - 不复用线程
* - 性能差
*/
/**
* 解决方案:配置线程池(见上文)
*/
💻 实战案例
案例1:用户注册
/**
* 用户注册(完整流程)
*/
@Service
public class UserRegisterService {
@Autowired
private UserMapper userMapper;
@Autowired
private EmailService emailService;
@Autowired
private SmsService smsService;
@Autowired
private LogService logService;
/**
* 注册用户
*/
@Transactional
public void register(RegisterRequest request) {
// 1. 保存用户(同步,需要事务)
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPhone(request.getPhone());
user.setCreateTime(new Date());
userMapper.insert(user);
// 2. 发送欢迎邮件(异步)
emailService.sendWelcomeEmail(user.getEmail(), user.getUsername());
// 3. 发送短信通知(异步)
smsService.sendWelcomeSms(user.getPhone(), user.getUsername());
// 4. 记录操作日志(异步)
logService.log("用户注册", "userId=" + user.getId());
// 立即返回,不等待异步任务完成
}
}
@Service
public class EmailService {
@Async
public void sendWelcomeEmail(String email, String username) {
System.out.println("发送欢迎邮件:" + email);
// 模拟发送邮件
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("邮件发送完成");
}
}
@Service
public class SmsService {
@Async
public void sendWelcomeSms(String phone, String username) {
System.out.println("发送欢迎短信:" + phone);
// 模拟发送短信
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("短信发送完成");
}
}
@Service
public class LogService {
@Async
public void log(String action, String detail) {
System.out.println("记录日志:" + action + " - " + detail);
// 记录到数据库
Log log = new Log();
log.setAction(action);
log.setDetail(detail);
log.setCreateTime(new Date());
logMapper.insert(log);
}
}
案例2:批量处理
/**
* 批量导入用户
*/
@Service
public class UserImportService {
@Autowired
private UserService userService;
/**
* 批量导入
*/
public void batchImport(List<User> users) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (User user : users) {
// 异步导入每个用户
CompletableFuture<Void> future = userService.importUser(user);
futures.add(future);
}
// 等待所有任务完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
allFutures.join(); // 阻塞等待
System.out.println("批量导入完成");
}
}
@Service
public class UserService {
@Async
public CompletableFuture<Void> importUser(User user) {
try {
// 导入用户
userMapper.insert(user);
System.out.println("导入用户:" + user.getUsername());
} catch (Exception e) {
System.out.println("导入失败:" + user.getUsername());
}
return CompletableFuture.completedFuture(null);
}
}
🎉 总结
核心要点
1. 启用异步
@EnableAsync
2. 标记异步方法
@Async
3. 配置线程池
自定义ThreadPoolTaskExecutor
4. 返回值
void / Future / CompletableFuture
5. 注意事项
- 自调用失效
- 事务失效
- 配置线程池
记忆口诀
Spring异步很简单,
@Async注解来标记。
@EnableAsync要开启,
异步功能才生效。
自调用会失效,
代理对象才有效。
注入自己或拆分类,
解决自调用问题。
事务异步要分开,
新线程没有事务。
同步方法加事务,
异步方法发通知。
返回值有三种:
void无返回值,
Future可等待,
CompletableFuture最强大。
线程池要配置,
核心最大队列容量。
线程名和拒绝策略,
优雅关闭很重要。
异步提升性能,
用户体验更好。
邮件短信和日志,
适合异步来执行!
愿你的应用异步执行,性能飞速提升! ⚡✨