Spring 事务中使用多线程引起的诡异问题

背景

日常工作中,spring的事务是我们常使用到的功能,因为它可以保证ACID特性,而且更新DB记录后再通过多线程异步刷新本地内存的需求也很常见,本文就来看一种更新然后异步刷新本地内存的常见错误写法

错误根源

首先我简化下错误的java代码,如下所示:

// 线程池
ExecuteService executeService;
//保存学生记录的缓存
Map<long, User> allUsers;

//在事务中更新记录然后使用线程池异步更新本地缓存记录
@Transation(rollbackfor=Exception.class)
public void updateAndRefleshMemory(User user){
	dao.update(user);
	// 关键点: 通过线程池异步刷新本地内存,等到真正执行refresh方法时,事务可能提交,也可能未提及。
	executeService.submit(new Runnable(){
		refreshMemory();
	})
}
// 从db中获取记录并刷新本地内存
private void refreshMemory(){
	List<User> users = dao.selectAll();
	allUsers.putAll(users);
}

上面代码都已经有注释了,那么你有看出问题在哪里吗?
这个问题比较诡异的点就在于内存中的User数据有可能是新的记录也有可能是旧的记录,原因在于:
1.假设RefleshMemory方法是在事务提交commit之后执行的,那么内存中的User记录就是新的,那么业务没有任何问题
2.假设RefleshMomery方法执行的比较快,事务还没有提交就执行完了,那么因为事务的隔离性,也就是select不可能读取到未提交的事务的更改记录,所以此时内存中的User数据是旧的记录,这样就产生问题了

总结

这个问题的根源在于在spring事务中开启多线程去读取本次事务中要修改的记录,由于多线程中操作DB记录时,有可能事务已经提交,也有可能未提交,导致结果不确定,从而引起错误的结果,正常应该要修改成要完全等待事务提交之后再进行其他比如刷新本地内存的操作,这样你就可以确认刷新本地内存时事务肯定是已经提交的了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值