关于Spring事务的死锁问题,还请大神指教
先说下环境,使用springMVC ,mybatis,mysql(默认隔离级别)
上代码:
@Autowired
private UserMapper userDao;
@Transactional
@RequestMapping(value = "/trxAdd.do", method = RequestMethod.GET)
public void getAccountAdd(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("add trx start");
cdl.await();
Map result=Helper.initResponse();
User user=userDao.selectByPrimaryKey(1); //select * from langcom where uid=1 uid 为pk
if(user.getUsername().equals("h18x")){
user.setUsername("xxx");
user.setCash(88888l);
userDao.updateByPrimaryKey(user);
}else{
result.put("message", "Add事务执行失败");
}
logger.info("add end");;
Helper.restful(response, result);
}
@Transactional
@RequestMapping(value = "/trxRemove.do", method = RequestMethod.GET)
public void getAccountRemove(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("remove trx start");
cdl.await();
Map result=Helper.initResponse();
User user=userDao.selectByPrimaryKey(1);
if(user.getUsername().equals("h18x")){
user.setUsername("xxx");
user.setCash(200l);
userDao.updateByPrimaryKey(user);
}else{
result.put("message", "Remove事务执行失败");
}
logger.info("remove end");;
Helper.restful(response, result);
}
@RequestMapping(value = "/trxStart.do", method = RequestMethod.GET)
public void getAccountStart(HttpServletRequest request, HttpServletResponse response) throws Exception {
cdl.countDown();
logger.info("count start end");
Helper.restful(response, Helper.initResponse());
}
执行方式很简单,执行 trxadd.do trxRemove.do 后 在执行trxStart.do ,结果产死锁问题。
怀疑如下:
1.之前有想过,是否是事务开始时,查询selectByPrimaryKey()时,两个线程对单条记录加了S锁,后续在updateByPrimaryKey()方法时,要加X锁,导致需要对方释放S锁,所以产生死锁问题。但是 使用双线程进行debug,其中add先执行至条件判断(即已经读取,猜测获取到了 S锁) ,然后换另一个线程执行update语句,毫无问题。所以之前猜想失败,select时,是没有S锁的加持。
2. 后续使用mysql客户端,开启事务,(
begin;
update langcom set cash=100 where uid= 1;
)并不提交,然后执行trxadd.do, 发现selectByPrimaryKey()查询毫无问题,但是在执行updateByPrimaryKey()方法时,会因为请求X锁挂起,但由于Spring的机制,会有一个事务超时的异常,而终止该线程。
那么问题来了,到底是什么导致了,双线程并行争抢导致的死锁问题呢?还请大神指教