几百个jvm中只有一个会竞争锁成功,剩下99个会一直阻塞等待。第一个释放锁后,也会重新唤醒99个锁,如果临时节点一旦被删除,又给每一个jvm发出通知竞争锁,唤醒99个jvm竞争的成本非常高。
所以应该采用:
基于临时顺序节点实现临时顺序节点:
只能按照01,02,03的顺序,也就是01会被首先竞争锁成功,然后只有01释放后,02才能获取锁成功,02会通过订阅01监听,队列形式,串行化。01消失后,02就是最小的,所以02获取锁就会成功。这一锁相当于我们的公平锁。
jvm01,jvm02,jvm03的顺序,当唤醒的时候,就会按照01,02,03的顺序来竞争锁,非常公平,防止了所有锁唤醒成本过高,以及阻塞问题。
什么是羊群效应?
当 jvm 释放锁的时候,会唤醒正在等待的 jvm 从新进入到获取锁的状态。如果正在阻塞的等待获取锁的 jvm,如果有几十个或者几百个、上千个的情况下ZkServer 端唤醒所有正在等待的 jvm,从新进入到获取锁的状态,唤醒的成本是非常高有可能会造成我们 ZkServer 端阻塞。
基于 ZK 实现分布式锁两种实现方案
1.多个 jvm 同时在 zk 上创建一个相同的临时节点,谁能够创建成功,谁就可以拿到这把锁:
存在羊群效应的 bug;
2. 基于临时顺序编号节点实现 多个 jvm 同时创建一个临时顺序编号节点,如果当前 jvm 创建的临时顺序编号节点是最小的情况下,则表示获取说成功643如果不是最小的情况下,则表示获取锁失败,就会进入到阻塞状态;当前的 jvm 订阅到我们上一个节点;
核心思想:当我们 jivm 释放锁的时候,不会通知一群正在等待获取锁的 vm,而是只会通知一个 jvm 获取锁,效率变高,对我们 zkserver 端 减去事件通知的压力
多个 jvm 抢锁,最终只会有一个 jvm 能够抢成功。
类似于: juc 并发编程中 公平锁实现原理。
伪代码实现:
1.多个 jvm 同时在 zk/lockPath 创建一个临时顺序编号节点。
2.每个 jvm 都能够创建到自己独立的临时顺序编号节点。
3.举个例子:
Jvm01 /lockPath01Jvm02 /lockPath02
Jvm03 /lockPath03
获取锁的流程:
Jvm01----
1.获取当前 jvm 创建的临时编号节点 lockPath01
2. 查询到 lockPath 下所有的子节点,实现排序 查找到最小的。3.最小的临时顺序编号节点: lockPath01
lockPath01==lockPath01 如果是小的节点情况下,则表示获取锁成功
Jvm02----
1.获取当前 jvm 创建的临时编号节点 lockPath02
2.查询到 lockPath 下所有的子节点,实现排序 查找到最小的
3.最小的临时顺序编号节点: ockPath01
4. lockPath02! =lockPath01
5. 如果当前自己创建的临时顺序编号节点不是最小的情况下,则会直接阻塞。
6.Jvm02 订阅到 lockPath01 该临时顺序编号节点。
唤醒之后:
Zk 服务器端,当 lockPath01 节点被删除之后,会通知给 jvm02Jvm02 从阻塞状态到运行状态。
1.获取当前 jvm 创建的临时编号节点 lockPath02
2.查询到 lockPath 下所有的子节点,实现排序 查找到最小的3.最小的临时顺序编号节点: lockPatho2
4. lockPath02==lockPath02 属于最小节点
5.获取锁成功。