当@Transtctional遇到@Async遇碰撞出怎样的火花?

上篇讲述了@Transtctional下调用@Async异步执行,然后token失效的问题:

记一次SpringCloud OpenFeign 服务调用传递 token @Async 上下文信息获取失败

本篇则详细讲述两者互存的各种场景及其使用注意事项。

一、@Transtctional @Async概念

@Transactional注解是Spring框架提供的一个用于声明式事务管理的注解。它可以应用在方法或类上,用于标识需要进行事务管理的方法或类。通过使用@Transactional注解,我们可以更加方便地管理事务,保障数据的一致性和可靠性

@Async 是 Spring 框架提供的注解,用于将方法标记为异步执行。通过使用 @Async 注解,可以告知 Spring 在调用被注解的方法时,使用新的线程或线程池进行异步执行。

二、调用场景复现,加入注解看是否回滚?

说明: @Transaction方法 服务于 saveInspectInfo() ;@Async 服务于testAsync()两者位于不同的两个类里边

1)saveInspectInfo() 加注解;testAsync()无注解

@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveInspectInfo() {
		taskInspectMapper.updateDataStatus(126387703699750912L);
		asyncTest.testAsync();
	}

public void testAsync(){
		log.info("testAsync threadName="+Thread.currentThread().getName());
		TaskInspect taskInspect=new TaskInspect();
		taskInspect.setProject("sss");
		taskInspectMapper.insertSelective(taskInspect);
	}

在这里插入图片描述
.updateDataStatus(126387703699750912L) 的dataStatus更新为3;但是执行第二个方法的时候失败了,最终结果 回滚成功,说明事物生效;
在这里插入图片描述

2)saveInspectInfo() 加注解;testAsync()也加注解

2-1) saveInspectInfo() 执行成功;testAsync() 执行失败,是否回滚?

@Async("asyncServiceExecutor")
	public void testAsync(){
		log.info("testAsync threadName="+Thread.currentThread().getName());
		TaskInspect taskInspect=new TaskInspect();
		taskInspect.setProject("sss");
		taskInspectMapper.insertSelective(taskInspect);
	}

在这里插入图片描述
我们可以看到 testAsync()开启了一个新的线程来执行的,最终结果是.updateDataStatus(126387703699750912L) 的dataStatus更新为3,回滚失败。
在这里插入图片描述

2-2)saveInspectInfo() 执行失败;testAsync() 执行成功,是否回滚?

@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveInspectInfo() {
		taskInspectMapper.updateDataStatus(126387703699750912L);
		asyncTest.testAsync();
		TaskInspect taskInspect=new TaskInspect();
		taskInspect.setProject("sss");
		taskInspectMapper.insertSelective(taskInspect);
	}

@Async("asyncServiceExecutor")
	public void testAsync(){
		log.info("testAsync threadName="+Thread.currentThread().getName());
		taskInspectMapper.updateDataStatus(126979559810678784L);
	}

在这里插入图片描述
通过结果可以看到两个更新方法都走了,两个线程,猜测结果是咋样的?那肯定是一个成功一个失败呗。有事物的回滚,异步的执行成功。
在这里插入图片描述

2-3)saveInspectInfo() 执行失败,还未触发到异步操作。

@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveInspectInfo() {
		taskInspectMapper.updateDataStatus(126387703699750912L);
		TaskInspect taskInspect=new TaskInspect();
		taskInspect.setProject("sss");
		taskInspectMapper.insertSelective(taskInspect);
		asyncTest.testAsync();
	}

在这里插入图片描述
testAsync()并未执行。所以两者状态都不变。

三、我就想让方法A执行完毕之后再异步执行方法B,如何处理?

就拿刚才的偏激场景来说事,我就想着方法A中的下单操作成功完成之后再走方法B,扣减库存。

@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveInspectInfo() {
		//事物操作方法1
		taskInspectMapper.updateDataStatus(126387703699750912L);
		//异步方法操作
		asyncTest.testAsync();
		//回滚
		TaskInspect taskInspect = new TaskInspect();
		taskInspect.setProject("sss");
		//事物操作方法2
		taskInspectMapper.insertSelective(taskInspect);
		//事物操作方法3
		taskInspectMapper.updateDataStatus(126389155885236224L);
	}

如上代码,执行结果:操作方法1执行成功,但是回滚;异步操作执行成功;操作方法2失败导致的事物回滚;操作方法3未执行。
在这里插入图片描述
我想要的结果并非如此,想象的是,三个事物操作方法都执行成功了,才执行异步操作

// 给当前线程注册一个事务同步器
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
			@Override
			public void afterCompletion(int status) {
				// 保证事务一定是已经提交了
				if (TransactionSynchronization.STATUS_COMMITTED == status) {
					// 异步方法写这里
					.......
				}
			}
		});
@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveInspectInfo() {
		//事物操作方法1
		taskInspectMapper.updateDataStatus(126387703699750912L);

		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
			@Override
			public void afterCompletion(int status) {
				// 保证事务一定是已经提交了
				if (TransactionSynchronization.STATUS_COMMITTED == status) {
					//异步方法操作
					asyncTest.testAsync();
				}
			}
		});
		//回滚
		TaskInspect taskInspect = new TaskInspect();
		taskInspect.setProject("sss");
		//事物操作方法2
		taskInspectMapper.insertSelective(taskInspect);
		//事物操作方法3
		taskInspectMapper.updateDataStatus(126389155885236224L);
	}

虽然按照顺序,我的异步注解在操作方法1后边,比较靠前,但是不影响我等待整个事物的成功执行后再提交。
在这里插入图片描述

四、两者碰撞注意事项

1)方法A和方法B不能同处于同一个类中。

因为 @Transactional 和 @Async 注解的实现都是基于 Spring 的 AOP ,而 AOP 的实现是基于动态代理实现的,故Async 失效的原理和原理是一样的。
所以要想两个注解都生效,必须保证是不同的类,保证是通过代理类去执行操作。

2)方法B尽量不要依赖方法A的相应操作。

举例说明:比如 方法A我执行了下单操作,想着用异步的方式去扣减库存,这种方式是严格禁止的,风险太大了。就是拿最偏激的场景来说一下这个事情。

3)异步方法尽量写到方法最后面,这样按照顺序,假如中间某个方法失败了,直接就回滚了,不会走到异步方法内,假如某个场景,异步注解就在中间或者偏上位置,那么就直接加事物同步器就可。

相关推荐

  1. 计算机视觉+人工智能碰撞火花

    2024-06-05 23:20:07       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-05 23:20:07       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-05 23:20:07       20 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-05 23:20:07       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-05 23:20:07       20 阅读

热门阅读

  1. 陪玩小程序都需要怎么做?

    2024-06-05 23:20:07       8 阅读
  2. 【Linux】GNU编译器基础-GDB

    2024-06-05 23:20:07       10 阅读
  3. VS_图片转换点云

    2024-06-05 23:20:07       8 阅读
  4. Python模块导入的写法

    2024-06-05 23:20:07       8 阅读
  5. 【Python音视频技术】用moviepy实现图文成片功能

    2024-06-05 23:20:07       11 阅读
  6. 【Docker学习】docker login/logout

    2024-06-05 23:20:07       9 阅读
  7. mysql 根据经纬度计算距离

    2024-06-05 23:20:07       7 阅读
  8. MFC 用Imm类库实现输入法修改输入模式

    2024-06-05 23:20:07       7 阅读
  9. N叉树的层序遍历-力扣

    2024-06-05 23:20:07       8 阅读