问:@Async和@Transaction可以一起使用吗?

 

 在Java中,@Async 和 @Transaction注解是可以一起使用的,但需要注意一些细节和潜在问题。

 

1. @Async 和 @Transactions 注解

  @Async注解:用于异步执行方法。使用此注解的方法会在单独线程中执行,而不会阻塞调用线程。在需要执行耗时操作而不希望阻塞主线程时非常有用。

 

  @Transactional注解:用于声明方法的事务性,通常用于数据库的操作,以确保方法的执行具有原子性。事务可以控制多个数据库操作的提交和回滚,以确保数据的一致性。

 

  一起使用注意事项:

   1) 异步方法是在独立的线程中执行的,而事务是与线程绑定的。故,@Async 注解的方法通常不会继承调用方线程的事务上下文

    若想在异步方法中使用事务,需要在异步方法内重新开启一个新的事务(重新定义 @Tranactional)。

   2) 需要确保异步方法通过 Spring 代理调用,不能再同一个类中直接调用,否则异步机制起不到作用。需要通过 Spring 容器获取的 Bean 来调用这些方法。

 

2. 示例代码

  场景:需要从数据库读取数据,进行处理,然后将结果异步返回保存数据库。

    设:从程序中读取用户数据,进行处理数据,然后异步将结果保存到另一个表中。(希望数据处理在事务中完成,以确保数据一致性,但希望保存操作是异步的,以提高性能。)

  1) 实体类(用户和处理后的结果实体类)

@Entity
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
}
@Entity
@Data
public class ProcessedResult {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long userId;
    private String resultData;
}


  2) 定义两个仓库接口,访问数据库

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

@Mapper
public interface ProcessedResultMapper extends BaseMapper<ProcessedResult> {
}

  

  3) 服务类中,结合 @Transactional 和 @Async 注解实现业务逻辑。

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ProcessedResultMapper processedResultMapper;

    @Autowired
    private AsyncService asyncService;

    @Transactional
    public void processUsers() {
        // 获取所有用户
        List<User> users = userMapper.selectList(null);

        for (User user : users) {
            // 数据处理
            String resultData = processData(user);

            // 异步保存处理结果
            asyncService.saveProcessedResult(user.getId(), resultData);
        }
    }

    private String processData(User user) {
        // 数据处理逻辑
        return "Processed data for user: " + user.getName();
    }
}

 

  4) 异步服务类。使用 @Async 注解异步保存结果。

@Service
public class AsyncService {

    @Autowired
    private ProcessedResultMapper processedResultMapper;

    @Async
    @Transactional
    public void saveProcessedResult(Long userId, String resultData) {
        // 创建并保存处理结果
        ProcessedResult processedResult = new ProcessedResult();
        processedResult.setUserId(userId);
        processedResult.setResultData(resultData);
        processedResultMapper.insert(processedResult);

        System.out.println("保存 processed result 的 user ID: " + userId);
    }
}

 

  5) 确保在配置类中启用异步支持。

@Configuration
@EnableAsync
public class AsyncConfig {

    /**
     * 定义一个线程池执行器,用于执行标记了 @Async 注解的异步方法。
     * 
     * @return Executor 线程池执行器实例
     */
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);    // 核心线程数
        executor.setMaxPoolSize(5);    // 最大线程数
        executor.setQueueCapacity(500);    // 任务队列的容量
        executor.setThreadNamePrefix("AsyncThread-");    // 线程池中线程的名称前缀
        executor.initialize();    // 初始化线程池
        return executor;
    }
}

 

  执行流程:

   UserService.processUsers() 方法使用 @Transactional 注解,确保在读取和处理用户数据时的事务一致性。
   处理每个用户,结果通过 AsyncService.saveProcessedResult() 方法异步保存。同时使用 @Transactional 注解,故每次保存操作都在独立的事务内执行。
   而 saveProcessedResult 方法时异步的,因此不阻塞主线程,从而提高了性能。


3. 总结

  1) @Transactional 注解主要用于确保数据一致性和操作的原子性。@Async 注解用于提到性能。结合注解使用,需注意事务的上下文的边界和线程的管理。

  2) 为了获取性能和一致性,需要在异步方法内部定义新的事务。

 

posted @ 2024-08-05 20:30  学Java的`Bei  阅读(288)  评论(0编辑  收藏  举报