1、什么是乐观锁
我认为就是:很乐观,每次去哪拿数据的时候都认为别人不会修改数据,所以不会上锁。但是在提交更新的时候会判断一下在这期间有没有其他人更新这个数据。
2、乐观锁的原理
由CAS机制+版本机制来实现。
数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。
(1)CAS机制:当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败。CAS 有效地说明了“ 我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可“。
(2)版本机制:CAS机制保证了在更新数据的时候没有被修改为其他数据的同步机制,版本机制就保证了没有被修改过的同步机制(意思是上面的ABA问题)。
3、乐观锁使用场景
3.1、解决幂等性问题(超卖,充值问题)
3.1.1、购票(超卖)
购票实现流程如下:
step1:查询是否有票,有票的 话,继续下一步,否则提示无票,结束;
step2:从用户账户扣除票款;
step3:余票减一操作;
这里的话,正常情况没问题,但是比如用户连续多点了几次,或者网络问题导致的再或者多人同时购买的时候的并发情况下,step1步骤会有两个或者多个线程同时进入,这时候判断都是有票的,然后继续进入step2,step3,这时候,就可能会出现余票负数,多卖的情况;
3.1.2、充值问题
充值实现流程如下:
step1:用户输入充值金额,请求后端业务系统;
step2:后端生成订单,订单状态是未支付,然后再请求第三方支付接口;
step3:用户端确认支付;
step4:第三方支付通过我方提供的回调接口异步通知支付结果;
这个step4是有缺陷的,假如第三方支付系统问题或者网络问题,有多个线程同时执行进入 ,根据订单id查询订单信息,发现status状态都是未支付,所以都进入if里面,这时候就出现了账户重复充值的情况;
4、如何实现乐观锁
乐观锁一般来说有以下2种方式:
- 使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
- 使用时间戳(timestamp)。乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
代码下载地址: 源码地址