⚡ Spring @Async异步方法:让任务并行执行!

31 阅读7分钟

副标题:异步编程的正确姿势,性能提升的秘密武器!🎯


🎬 开场:同步 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最强大。

线程池要配置,
核心最大队列容量。
线程名和拒绝策略,
优雅关闭很重要。

异步提升性能,
用户体验更好。
邮件短信和日志,
适合异步来执行!

愿你的应用异步执行,性能飞速提升! ⚡✨