@Transactional 和 @Async 标注同一个 service 方法会导致事务失效吗
公众号:认知科技技术团队阿里Java 面试:@Transactional 和 @Async 标注同一个 service 方法会导致事务失效吗
现介绍下@Transactional 和 @Async 标注的不同方法是否可以一起使用(相互调用)?
@Transactional 和 @Async 标注的方法可以相互被调用,但需要注意一些关键事项以确保它们按预期工作。
@Transactional 是 Spring 框架中的一个注解,用于声明一个方法或类需要事务支持。当这个方法被调用时,Spring 会为这个方法的执行创建一个新的事务或者加入一个现有的事务,如果它正在一个事务上下文中运行,但主要行为是由事务的传播行为决定。
@Async 是 Spring 中用于异步执行的注解。当你标记一个方法为 @Async,Spring 会在一个单独的线程中异步地执行这个方法。
当你结合使用 @Transactional 和 @Async 时,你需要确保事务边界正确地管理。由于 @Async 方法会在一个单独的线程中执行,如果你没有正确地配置事务传播行为,可能会出现问题。
在 @Async 方法中调用 @Transactional 方法
如果我们在 @Async 方法中调用 @Transactional 方法,Spring 会正确地管理事务并传播其上下文,从而确保数据一致性。
@Async
public void transferAsync(Long depositorId, Long favoredId, BigDecimal amount) {
transfer(depositorId, favoredId, amount);
// other async operations, isolated from transfer
}
@Transactional
public void transfer(Long depositorId, Long favoredId, BigDecimal amount) {
Account depositorAccount = accountRepository.findById(depositorId)
.orElseThrow(IllegalArgumentException::new);
Account favoredAccount = accountRepository.findById(favoredId)
.orElseThrow(IllegalArgumentException::new);
depositorAccount.setBalance(depositorAccount.getBalance().subtract(amount));
favoredAccount.setBalance(favoredAccount.getBalance().add(amount));
accountRepository.save(depositorAccount);
accountRepository.save(favoredAccount);
}
从 @Async
方法中调用 @Transactional
方法可以提高性能,因为它允许与调用线程并行执行特定内部操作,而不会造成数据不一致。
在@Transactional 方法中调用 @Async 方法
Spring 目前使用 ThreadLocal 来管理当前线程的事务。因此,它不会在不同线程之间共享线程上下文。
因此,如果一个使用 @Transactional 注解的方法调用了一个使用 @Async 注解的方法,Spring 不会传播相同的事务线程上下文。
这意味着,在异步方法中执行的事务不会与调用线程的事务共享相同的上下文。每个线程都有其自己的 ThreadLocal 变量副本,这意味着每个线程都有其自己的事务上下文。当从事务方法调用异步方法时,需要特别注意这一点,以确保数据的一致性和完整性。如果需要保持事务的上下文,可能需要采取额外的措施,如使用特定的传播行为或捕获并处理异步方法中可能发生的异常。
因此,为了避免这种数据一致性问题,我们必须避免从 @Transactional 方法中调用 @Async 方法,因为不会发生线程上下文传播。
总之,@Transactional 和 @Async 标注的方法可以被相互调用,但需要确保你了解并正确处理了相关的复杂性和潜在问题。