关于分布式锁的几种常见实现方式详解

众所周知 再单机环境下 如果有不同线程对共享资源进行操控的时候 可以使用synchronize关键字来保证线程的安全性 但是在分布式的环境下该方法就会失效 为了保证在分布式的环境下不同客户端在对同一个共享资源的安全性访问时 我们会需要使用到分布式锁 目前 常用的分布式锁有三种 一种是基于数据库的 一种是基于Redis的 还有一种是基于zookeeper的。

数据库的方法实现最为简单 我们可以构建一个表 里面有主键 方法名与更新时间 且对方法名做出唯一性约束 、

在进行锁的时候使用insert进行插入 在释放锁的时候使用delete删除该行 那么在有多个请求同时访问的时候 只有最第一个请求可以执行成功 后续的都会插入失败  不过这样做也会带来几个弊端

其一就是会加大数据库的压力 因为牵扯到频繁的访问数据库 所以数据库的性能必须要好  必要时可以搭建集群

其二就是其锁是非阻塞式的(这里扯到阻塞非阻塞 如果对这个概念不明白的话可以往后面看 我会做出补充 ) 如果插入失败不会做等待直接返回错误 只能使用轮询或其他方法 势必会影响程序的性能 

其三就是这把锁没有失效时间 如果在插入完成后服务器宕机 那么记录变会一直存在不可删除 导致后续所有请求都获取不到锁 

最后就是对数据库的严重依赖 如果数据库不可用那么整个服务都会挂掉 当然还有该锁为非公平锁 所以等待请求都是凭借有运气去获取该锁等缺点  不过这些弊端所有的弊端都可以通过一些优化进行解决 并不是不可避免的

基于Redis的话会有几个常用的命令

SetNx key value  当且仅当key不存在的会存入一个key为value的字符串 返回1 若存在 则返回0

expire 为key设置一个超时的时间 时间到后会自动释放锁 避免死锁

delete key 删除key

使用过程为先setNx判断返回值 如果为0就占有锁 且通过explre设置超时时间避免死锁 最后程序执行完成使用delete删除key释放锁

这样一般情况下可以满足正常的实际需求 不过也会出现使用setNx方法执行后 expire执行前系统宕机 那么还是会产生死锁的现象

那么如何优化最大程度的避免死锁呢 这里我们需要用到getSet(Key,NewValue)方法 该方法为 返回获取键为key的值 如果没有返回null 且把NewValue作为新值传递把原先的值覆盖 

1:先使用setNx(lockKey,当前时间+失效时间)若返回1即获取锁成功 若返回0 则失败执行下一步操作

2:使用get方法获取到锁 并判断时间是否小于当前时间 如果是 则该锁已经失效 可以进行重新操作

3:使用getSet(lockkey,新的当前时间+过期时间)返回值与步骤1的oldCurrentTime相比较 如果相等则说明当前getSet设置成功 获取到锁 如果不相等 代表该锁又被其他请求抢占 可以继续等待或返回失败

最后在获取到锁执行程序完成后可以通过自己程序的执行时间比较设置的失效时间 如果小于 则使用delete 释放锁 如果大于则不需要进行任何操作

最后为使用zookeeper实现分布式锁

创建一个目录 mylock 线程如果想要获取锁就必须在该目录下弄一个临时顺序节点 在判断该节点是否为最小节点 如果是就拿到锁 完成程序后删除 

这个时候如果另一个线程进入 判断自己不是最小顺序节点 就给自己次级小的节点设置监听事件 当稍小一级的节点被删除时线程获取到监听后拿到锁

最后再来聊一聊阻塞非阻塞吧 阻塞的意思就是当一个请求后发出 并没有立即得到相应 那么程序就会一直再此等待 直到结果的返回 非阻塞就是请求发出后如果没有立即得到结果该线程也不会被挂起 而是可以执行其他操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值