ReentrantLock 在@Transactional事务注解内的Service方法失效

业务场景如下:

在单体应用高并发场景, 全校所有学生开始选课, 选课大家都是抢着选择热门的选修课的, 所以会产生高并发, 热门课程的席位是有限的, 比如某个热门课程仅限 100 名学生, 但是高并发场景下,执行以下代码, 经常超过100名学生抢到该热门课程

以下代码只是大概, 并非可完整执行, 只是用于解释问题;

//可重入锁,默认是非公平锁
private Lock lock = new ReentrantLock(true);

@Autowired
private CourseMapper courseMapper;

@Autowired
private CourseStudentMapper courseStudentMapper;

@Override
@Transactional
public Result updateCourseNum(String courseId, String studentId) {
	try {
		//这里加锁了, 但是总是会超过100个学生能抢到热门课程, 难道锁不起作用、lock是同一个对象
		lock.lock();
		//getCourseNumber的SQL语句大概: "SELECT number FROM course WHERE course_id = ? ";
		Course course = courseMapper.getCourseNumber(String courseId);
		
		Integer number = course.getNumber;
		
		if( number > 0  ){
			// updateCourseNumber的SQL语句大概 "UPDATE course SET number = number-1 WHERE course_id=?";
			//课程席位数减1
			courseMapper.updateCourseNumber(courseId);
			//关联课程与学生的关系
			courseStudentMapper.saveRelativeCourseStudent(courseId,studentId)
		}else{
			return Result.error("课程席位已满人");
		}
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
		//释放锁
		lock.unlock();
	}
	return Result.ok("恭喜您,成功选到您心仪的课程");
}

问题分析:
service是单例的lock锁的是同一个对象, 没问题, 我的又不是分布式应用, 只是一个单体应用; 最终把问题锁定在事务上, 不加@Transactional没问题, 加了@Transactional就出现问题了

这里我简单说一下事务@Transactional的原理, 真的很简单, 就一张图
在这里插入图片描述
请看下入解释:

在这里插入图片描述

  • 脏读
    脏读发生在一个事务A读取了被另一个事务B修改,但是还未提交的数据。假如B回退,则事务A读取的是无效的数据。
  • 原因就是:
    当 number = 1 的时候 , 最后一个席位, 线程A 先进来抢到了席位, 线程A业务处理完成之后, 释放了锁, 但是 事务还未提交, 数据的 course表的number = 1 , 此时线程B在线程A释放锁之后, 立刻进入一顿操作猛如虎, 查询数据库course的number=1; 并且处理业务, 线程B也抢到的席位, 线程B提交事务, 此时线程A也提交了事务; 最终到时 number值为 -1 了 , 超出课程席位了
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值