微信网站开发有中院管辖呢,建设公司网站价格,什么是网站后台,wordpress页面使用方法转载自 漫画#xff1a;什么是分布式锁分布式锁的实现有哪些#xff1f;1.Memcached分布式锁利用Memcached的add命令。此命令是原子性操作#xff0c;只有在key不存在的情况下#xff0c;才能add成功#xff0c;也就意味着线程得到了锁。2.Redis分布式锁和Memcached的方式…转载自 漫画什么是分布式锁分布式锁的实现有哪些1.Memcached分布式锁利用Memcached的add命令。此命令是原子性操作只有在key不存在的情况下才能add成功也就意味着线程得到了锁。2.Redis分布式锁和Memcached的方式类似利用Redis的setnx命令。此命令同样是原子性操作只有在key不存在的情况下才能set成功。setnx命令并不完善后续会介绍替代方案3.Zookeeper分布式锁利用Zookeeper的顺序临时节点来实现分布式锁和等待队列。Zookeeper设计的初衷就是为了实现分布式锁服务的。4.ChubbyGoogle公司实现的粗粒度分布式锁服务底层利用了Paxos一致性算法。如何用Redis实现分布式锁Redis分布式锁的基本流程并不难理解但要想写得尽善尽美也并不是那么容易。在这里我们需要先了解分布式锁实现的三个核心要素1.加锁最简单的方法是使用setnx命令。key是锁的唯一标识按业务来决定命名。比如想要给一种商品的秒杀活动加锁可以给key命名为 “lock_sale_商品ID” 。而value设置成什么呢我们可以姑且设置成1。加锁的伪代码如下 setnxkey1当一个线程执行setnx返回1说明key原本不存在该线程成功得到了锁当一个线程执行setnx返回0说明key已经存在该线程抢锁失败。2.解锁有加锁就得有解锁。当得到锁的线程执行完任务需要释放锁以便其他线程可以进入。释放锁的最简单方式是执行del指令伪代码如下delkey释放锁之后其他线程就可以继续执行setnx命令来获得锁。3.锁超时锁超时是什么意思呢如果一个得到锁的线程在执行任务的过程中挂掉来不及显式地释放锁这块资源将会永远被锁住别的线程再也别想进来。所以setnx的key必须设置一个超时时间以保证即使没有被显式释放这把锁也要在一定时间后自动释放。setnx不支持超时参数所以需要额外的指令伪代码如下expirekey 30综合起来我们分布式锁实现的第一版伪代码如下ifsetnxkey1 1{expirekey30try {do something ......} finally {delkey}
}好端端的代码怎么就回家等通知了呢因为上面的伪代码中存在着三个致命问题1. setnx和expire的非原子性设想一个极端场景当某线程执行setnx成功得到了锁setnx刚执行成功还未来得及执行expire指令节点1 Duang的一声挂掉了。这样一来这把锁就没有设置过期时间变得“长生不老”别的线程再也无法获得锁了。怎么解决呢setnx指令本身是不支持传入超时时间的幸好Redis 2.6.12以上版本为set指令增加了可选参数伪代码如下setkey130NX这样就可以取代setnx指令。2. del 导致误删又是一个极端场景假如某线程成功得到了锁并且设置的超时时间是30秒。如果某些原因导致线程A执行的很慢很慢过了30秒都没执行完这时候锁过期自动释放线程B得到了锁。随后线程A执行完了任务线程A接着执行del指令来释放锁。但这时候线程B还没执行完线程A实际上删除的是线程B加的锁。怎么避免这种情况呢可以在del释放锁之前做一个判断验证当前的锁是不是自己加的锁。至于具体的实现可以在加锁的时候把当前的线程ID当做value并在删除之前验证key对应的value是不是自己线程的ID。加锁
String threadId Thread.currentThread().getId()
setkeythreadId 30NX解锁
ifthreadId .equals(redisClient.get(key)){del(key)
}但是这样做又隐含了一个新的问题判断和释放锁是两个独立操作不是原子性。我们都是追求极致的程序员所以这一块要用Lua脚本来实现String luaScript if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;redisClient.eval(luaScript , Collections.singletonList(key), Collections.singletonList(threadId));这样一来验证和删除过程就是原子操作了。3. 出现并发的可能性还是刚才第二点所描述的场景虽然我们避免了线程A误删掉key的情况但是同一时间有AB两个线程在访问代码块仍然是不完美的。怎么办呢我们可以让获得锁的线程开启一个守护线程用来给快要过期的锁“续航”。当过去了29秒线程A还没执行完这时候守护线程会执行expire指令为这把锁“续命”20秒。守护线程从第29秒开始执行每20秒执行一次。当线程A执行完任务会显式关掉守护线程。另一种情况如果节点1 忽然断电由于线程A和守护线程在同一个进程守护线程也会停下。这把锁到了超时的时候没人给它续命也就自动释放了。守护线程的代码并不难实现有了大体思路大家可以自己尝试实现一下。