现有领券流程存在两个问题:
现象:并发情况下,优惠券超发的问题,用户超领的问题
原因:整个流程里存储一系列的校验,校验通过之后才能领券,涉及了多次校验与数据库操作,这些操作不是原子性
并发问题的通用解决方法:
通常情况下,只要是非原子性的操作,在高并发情况下都会出现并发安全问题。一般会使用加锁的方式解决问题
乐观锁:
比较乐观看问题的一种思想,
认为其它线程很少会修改我的数据,所以我不需要加排它性的、独占性的锁,因为会影响性能。
但是为了防止真的有其它线程修改数据,导致出现问题:利用CAS线程在每次修改之前先对原数据进行校验
如果数据没有被其它线程修改,就执行自己的修改操作
如果数据被其它线程修改了,就放弃操作
优点:性能好,安全性也好;缺点:如果写操作多,会出现比较高的失败率
适合:读多写少的情况
实现:对数据增加版本号,每次修改之前,先对比版本号,如果版本号不同,就说明被修改了
如果版本号相同,就执行修改操作,然后把版本号+1
悲观锁:
比较悲观看问题的一种思想
总是认为数据会被其它线程做修改操作,为了避免其它线程的改数据影响自己,对数据加排它性、独占性锁
自己对数据加锁,然后开始操作。在操作过程中,其它得不到锁,就需要等待
等待自己释放锁,其它线程才可以抢锁、得到锁再进行操作
把多线程并发变成了串行
优点:安全性高;缺点:性能不好
适合:写多读少的情况
实现:synchronized、Lock对象
超发与超领问题的解决
第一个问题,优惠券超发的问题:使用了优化后的乐观锁解决
update coupon set issue_num = issue_num+1 where issue_num < total_num and id = 优惠券id
只要已发放数量,没有达到总数量,就允许领取、修改数据
避免了原始乐观锁的方式,存在大量失败的问题
第二个问题,优惠券超领的问题:没办法使用乐观锁解决了
原因:先统计当前用户已经领取了多少张;再进行校验;如果校验通过再修改发放数量、领取用户券
涉及了不同表、不同数据的多次操作,不能使用乐观锁
解决:使用了悲观锁,使用synchronized关键字,锁住用户id
然后又出现了新的问题