三种分布式锁

----------本文为学习记录如有错误帮忙指正

一、什么是分布式锁?

        在单机系统下,如果多个线程同时访问一个变量或者代码片段就会产生多线程问题。(被访问的变量或者代码片段被称之为临界区域)这时我们就需要让所有线程按顺序一个一个执行对这个数据的操作就可以避免这个问题。但是在分布式的架构下,会有多台服务器同时运行,也就同时会有多个JVM运行,某一个JVM中的变量在其他JVM中是不可见的,所以简单的单机系统下的锁是无法解决分布式架构中的多线程的问题。我们就需要找到在多个JVM中都可见的锁来解决这个问题。这时就需要分布式锁。

二、三种分布式锁

        三种分布式锁分别为基于数据库实现的乐观锁,基于redis实现的分布式锁,基于zookeeper实现的分布式锁。

1.基于数据库实现的乐观锁

        提到乐观锁就不得不提CAS(compare and swap)思想,顾名思义比较和交换,在准备做数据持久化时先比较数据库中的值与操作时的值是否相等,如果相等则进行修改,否则放弃操作或者进入循环。

        CAS存在几点问题

                1.如果长时间没有操作成功线程一直在循环中,对CPU的开销过大。

                2.只能保证一个变量的原子性操作。

                3.存在ABA问题。

                ABA问题如图

        当线程1读入A数据后,线程1进入阻塞状态或者处理业务过长。这时线程2读入数据,将A改成B,随后线程3读入数据,将B改成A,这时线程1开始存入数据,将线程1读到的数据与数据库中的数据进行对比发现相同,这时可以将A改成C 。

        表面上看好像并没有什么问题,但是遇到金融方面业务就会遇到问题。

        例如:我的银行卡上有100元,这时我要取50元,由于某种原因我点击两次按钮,有线程1和线程2同时操作,线程1取走我50元后,线程2进入阻塞状态,这时有人往我卡上打入50元,线程2开始执行操作,在最后发现我账户上为100元与当时查到的相同,第二次成功取走50元导致发生多线程问题。

        解决方法:加入版本号

        在数据库中加入新的一列version,在每一次update成功后都进行+1,这样就可以完美避免ABA问题。SQL为update 表明 set data = data, version = version + 1 where version = version。

2.基于redis的分布式锁

        redis分布式锁主要体现为对redis服务器中的key和value的抢占。当线程需要获取锁时就会向redis中插入一条key,value,如果插入成功就可以成功获取锁,如果redis中已经存在了该锁导致插入失败则获取锁失败。

        

        上面模式的redis分布式锁存在以下问题

        当线程1拿到锁后在执行业务时因为某些原因进入阻塞状态导致锁超时自动释放,这时线程2拿到锁,再线程2的业务没有执行完时,线程1从阻塞状态转换为运行状态,业务执行完成执行释放锁的代码,这是线程2拿到的锁被莫名奇妙的释放了,这就产生了问题。

        解决方案有两种:

                1.设置锁超时时间远大于业务执行时间

                2.为每个线程拿到锁时添加唯一标识(建议在key上存储业务名,在value上存储uuid),删除锁时要判断锁与当前线程是否对应

        针对解决方案2仍然存在问题:

                在判断key是否相等然后删除锁这并不满足原子性

        当线程1代码执行结束准备释放锁前判断锁和线程1是否对应,判断结束后线程1进入阻塞状态,还是会发生上面提到的问题。

        解决方案:

                将判断线程和锁是否对应和释放锁这两步操作放到Lua脚本中执行。

3.基于zookeeper的分布式锁

        zookeeper的分布式锁主要是利用了临时顺序节点的特性,使每个进来的线程按顺序执行。

        首先建立一个持久节点作为父节点,当有线程需要执行被锁控制的代码时会先查询父节点下面是否有其他子节点,如果没有则在父节点下面创建一个临时顺序节点作为子节点,获得锁。

        当线程2需要执行代码时,同样的还是会在父节点下创建一个临时顺序节点,然后判断自己是不是顺序最靠前的节点,如果不是则注册一个watcher用于监听前面的节点。之后进来的每一个节点都是监听其前面的一个节点。

        如果线程执行结束,则会删除自己的节点,后面监听他的节点发现其被删除后即可开始执行。 

         

        

        

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值