秒杀环境下:订单服务从库存中心拿到库存数,如果库存总数大于0,则进行库存扣减,并创建订单订单服务负责创建订单库存服务负责扣减库存
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传上传(im-2VhXDV2SB-1659072511507)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728222422315.png)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728222422315.png)]](https://1000bd.com/contentImg/2022/08/01/185112320.png)
java代码如下,以下代码可能会出现超卖或者重复卖的问题:

运行结果如下:

引入synchornized 或 ReentrantLock ,单体锁可以保证原子性;
上图这种情况,在单程序中是ok的,但是在分布式情况下会出现如下场景:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hg75ryby-1659072511508)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20220728223621408.png)]](https://1000bd.com/contentImg/2022/08/01/185113347.png)

分布式场景下,相当于一份业务代码copy了许多份,所以仅靠单体锁是无法解决以上问题的!
核心逻辑:
俗话说得好,没有什么问题是加一层解决不了的!
如果在各个服务之间,加一层公共资源,然后同时抢公共资源的锁,抢到的线程才能去执行对应业务逻辑,不就ok了吗?

抢到锁去执行,失败则自旋

当B执行完毕,将锁归还,则其他线程方可继续
抢资源的话,其实可以使用 setnx 命令来执行,因为只有当不存在时,才能写入这个key,其他线程如果晚一步,发现已经key已经被写入了,就知道自己不是第一个来的线程,所以就可以识趣的去自旋了;
对应java代码如下:

此时的运行结果就正常咯

如果某个线程在执行锁逻辑过程中宕机,导致没有删除锁
解决:
代码如下:

注!以上两种代码写法,一定要用第一种,防止线程恰好在他们之间死掉
如果某个线程在执行过程中卡顿,导致卡过了超时时间
这问题咱们解决不了,一会儿有请 redisson 来解决
该问题其实是锁不住问题的延续问题:当有线程A进入后由于超时,有其他线程B进入,此时redis中的锁是线程B的,而原来的线程A接着执行,线程A删除了别人的锁
解决:

使用lua表达式解决;
为什么有方法1了还要方法2呢?举个例子,当前线程恰好在这一步死了,(红线标注的位置)
那就会又有对应问题!

解决: 将注释掉的代码替换为:
代码的含义:
redis.call(‘get’, KEYS[1]) ---- 调用’lock’这个key对应的值
ARGV[1] ---- threadUuid这个值
如果以上两个值相同,则调用以下方法:
redis.call(‘del’, keys[1]) — 删除’lock’这个key
否则返回;
lua表达式是将被注释的这4行代码一样的逻辑,封装起来交给redis去做,可以保证原子性!就能解决“删除别人锁”的问题。
Redisson是Redis服务器上的分布式可伸缩Java数据结构----驻内存数据网格(In-Memory Data Grid,IMDG)。
底层使用netty框架,并提供了与java对象相对应的分布式对象、分布式集合、分布式锁和同步器、分布式服务等一系列的Redisson的分布式对象。



测试可重入锁

测试锁现象:

通过redis desktop manager可视化软件可以得到以下答案:
测试带时间的lock方法
redisson解决“删除别人锁”这个问题,采取的方法是抛出一个异常