一、简介
在日常开发过程中,大型的项目一般都会采用分布式架构,那么在分布式架构中若需要同时对一个变量进行操作时,可以采用分布式锁来解决变量访问冲突的问题,最典型的案例就是防止库存超卖,当然还有其他很多的控制方式如数据库乐观锁、redis实现分布式锁等,这篇文章我们讨论一下怎么使用ZooKeeper来实现分布式锁。
二、尝试写一个分布式锁
开发思路【实现一个公平锁】
在前面的文章中我们学习了ZooKeeper节点的概念,实现分布式锁的基本思路也是基于对节点的监听与操作从而实现的。
- 1、创建一个父节点/lockDemo,并对父节点设置监听事件,实际加锁的对象为父节点下的子节点【临时节点】。
- 2、当用户请求进来时,会在/lockDemo节点下创建临时顺序节点。
- 3、此时创建的节点会判断自己是不是/lockDemo下最小的节点。
- A、是最小的,获得锁
- B、不是最小的,对前一个节点添加watch监听事件
- 4、获得锁的请求在执行完业务代码之后,要释放锁【delete临时顺序节点】,此时后面的节点【对这个节点进行监听的后一个节点】会收到通知,并执行前面的获取锁逻辑。
PS:由于非公平锁会带来“羊群效应”,性能不高,所以这里就不考虑了。
PS:羊群效应:每次只有一个线程加锁成功,剩下的请求都在监听等待抢锁,当释放锁时这些监听的线程又一窝蜂的去抢锁,循环往复,会给zookeeper带来很大的效能压力。
三、代码实现
Curator客户端已经帮我们封装得很好了,我们并不需要按照开发思路中一步一步来写代码,只需要用现成的API即可,方便快捷且可靠!
由于环境配置、pom依赖、获取zk连接的工具类ZookeeperClientUtil等代码,在前面的文章已经写过了,这里就不重复写了,只放分布式锁的实现思路代码:
/** * 分布式锁测试类 * -- InterProcessMutex : Curator封装的分布式锁操作对象 * * @author 有梦想的肥宅 * @date 2021/08/18 */ public class CuratorLockDemo { public static void main(String[] args) { //1、创建两个分布式锁用于测试 InterProcessMutex lock1 = new InterProcessMutex(ZookeeperClientUtil.getCuratorFramework(), "/lockDemo"); InterProcessMutex lock2 = new InterProcessMutex(ZookeeperClientUtil.getCuratorFramework(), "/lockDemo"); //2、定义线程执行内容 new Thread(new Runnable() { @Override public void run() { try { //3、测试可重入性 lock1.acquire(); System.out.println("线程1 获取到锁"); lock1.acquire(); System.out.println("线程1 重入时获取到锁"); Thread.sleep(5 * 1000); lock1.release(); System.out.println("线程1 释放重入的锁"); lock1.release(); System.out.println("线程1 释放锁"); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { lock2.acquire(); System.out.println("线程2 获取到锁"); lock2.acquire(); System.out.println("线程2 重入时获取到锁"); Thread.sleep(5 * 1000); lock2.release(); System.out.println("线程2 释放重入的锁"); lock2.release(); System.out.println("线程2 释放锁"); } catch (Exception e) { e.printStackTrace(); } } }).start(); } }