curator的分布式锁实现和源码分析 Mrsunup

curator 本身提供了非常多的功能点,比如分布式锁,leader选举,类似java的CyclicBarrier,在这里不再列举了

这里主要讲解curator的分布式的锁的基本实现以及源码分析

1.curator的分布式锁实现

/**
 * @Project: 3.DistributedProject
 * @description:  curator的分布式锁的实现
 * @author: sunkang
 * @create: 2018-06-24 10:09
 * @ModificationHistory who      when       What
 **/
public class CuratorLocks {
    public static void main(String[] args) throws Exception {
        CuratorFramework curatorFramework  = CuratorFrameworkFactory.builder().connectString("192.168.44.129:2181")
                .sessionTimeoutMs(4000).retryPolicy(new ExponentialBackoffRetry(4000,3))
                .build();
        curatorFramework.start();
        //InterProcessMutex这个锁为可重入锁
        InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework,"/locks");
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            fixedThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    boolean flag = false;
                    try {
                        //尝试获取锁,最多等待5秒
                        flag = interProcessMutex.acquire(5, TimeUnit.SECONDS);
                        Thread currentThread = Thread.currentThread();
                        if(flag){
                            System.out.println("线程"+currentThread.getId()+"获取锁成功");
                        }else{
                            System.out.println("线程"+currentThread.getId()+"获取锁失败");
                        }
                        //模拟业务逻辑,延时4秒
                        Thread.sleep(2000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally{
                        if(flag){
                            try {
                                //释放锁
                                interProcessMutex.release();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            });
        }
    }
}

输出结果如下: 后面的两个,因为超时了,导致锁没有获取到

线程18获取锁成功
线程16获取锁成功
线程19获取锁成功
线程20获取锁失败
线程17获取锁失败

2.curator的分布式锁源码分析
可以先看这句话:

     InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework,"/locks");

最终进入到这个构造方法,主要进行了一些初始化变量的初始化,maxLeases的在后面的源码可以看到是监听比自己小一个的节点

   InterProcessMutex(CuratorFramework client, String path, String lockName, int maxLeases, LockInternalsDriver driver) {
        this.threadData = Maps.newConcurrentMap();
        this.basePath = PathUtils.validatePath(path);
      //path为/locks ,lockName 为“lock-”,maxLeases = -1,driver 为StandardLockInternalsDriver
        this.internals = new LockInternals(client, driver, path, lockName, maxLeases);
    }

然后看上面例子的代码写的获取锁的这句话,基于这句话,完成后面的分析

flag = interProcessMutex.acquire(5, TimeUnit.SECONDS);

实际到了调用了internalLock的方法

private boolean internalLock(long time, TimeUnit unit) throws Exception {
       Thread currentThread = Thread.currentThread();
       //获得当前线程的锁
       InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
       //如果锁不为空,当前线程已经获得锁,可重入锁,lockCount++
       if (lockData != null) {
         //主要做一个可重入锁,锁的调用次数增加1
           lockData.lockCount.incrementAndGet();
           return true;
       } else {
           //获取锁,返回锁的节点路径
           String lockPath = this.internals.attemptLock(time, unit, this.getLockNodeBytes());
           if (lockPath != null) {
               //向当前锁的map集合添加一个记录
               InterProcessMutex.LockData newLockData = new InterProcessMutex.LockData(currentThread, lockPath);
               this.threadData.put(currentThread, newLockData);
               return true;
           } else {
               return false;//获取锁失败
           }
       }
   }

下面是threadData的数据结构,是一个Map结构,key是当前线程,value是当前线程和锁的节点的一个封装对象。


private final ConcurrentMap<Thread, InterProcessMutex.LockData> threadData;
 
private static class LockData {
        final Thread owningThread;
        final String lockPath;
        //锁的调用次数,可以重入,每重入一次,增加1
        final AtomicInteger lockCount;
 
        private LockData(Thread owningThread, String lockPath) {
            this.lockCount = new AtomicInteger(1);
            this.owningThread = owningThread;
            this.lockPath = lockPath;
        }

由internalLock方法可看到,最重要的方法是attemptLock方法

String lockPath = this.internals.attemptLock(time, unit, this.getLockNodeBytes());
String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception {
        long startMillis = System.currentTimeMillis();
        //将等待时间转化为毫秒
        Long millisToWait = unit != null ? unit.toMillis(time) : null;
        byte[] localLockNodeBytes = this.revocable.get() != null ? new byte[0] : lockNodeBytes;
        //重试次数
        int retryCount = 0;
        String ourPath = null;
        boolean hasTheLock = false;
        boolean isDone = false;
 
        while(!isDone) {
            isDone = true;
 
            try {
                //在当前path下创建临时有序节点
                ourPath = this.driver.createsTheLock(this.client, this.path, localLockNodeBytes);
                //判断是不是序号最小的节点,如果是返回true,否则阻塞等待
                hasTheLock = this.internalLockLoop(startMillis, millisToWait, ourPath);
            } catch (NoNodeException var14) {
                if (!this.client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper())) {
                    throw var14;
                }
 
                isDone = false;
            }
        }
        //返回当前锁的节点路径
 
        return hasTheLock ? ourPath : null;
    }

下面来看internalLockLoop方法,判断是不是最小节点的方法

private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception {
        boolean haveTheLock = false;
        boolean doDelete = false;
 
        try {
            if (this.revocable.get() != null) {
                ((BackgroundPathable)this.client.getData().usingWatcher(this.revocableWatcher)).forPath(ourPath);
            }
            //自旋
            while(this.client.getState() == CuratorFrameworkState.STARTED && !haveTheLock) {
                //获取排序好的子节点,这里利用了Collections的Comparator的排序,感兴趣的可以往下看
                List<String> children = this.getSortedChildren();
                //得到临时节点的字符串
                String sequenceNodeName = ourPath.substring(this.basePath.length() + 1);
                //这里的driver是启动的时候设置的,具体实现为StandardLockInternalsDriver
                //判断是否可以获取锁,并设置监控节点到PredicateResults中
                PredicateResults predicateResults = this.driver.getsTheLock(this.client, children, sequenceNodeName, this.maxLeases);
                //判断是否是最小节点
                if (predicateResults.getsTheLock()) {
                    haveTheLock = true;
                } else {
                    //给比自己小的节点设置监听器
                    String previousSequencePath = this.basePath + "/" + predicateResults.getPathToWatch();
                    //同步,是为了实现公平锁
                    synchronized(this) {
                        try {
        //注册监听                            
    ((BackgroundPathable)this.client.getData().usingWatcher(this.watcher)).forPath(previousSequencePath);
                            //如果等待时间==null,一直阻塞等待
                            if (millisToWait == null) {
                                this.wait();
                            } else {
                                millisToWait = millisToWait - (System.currentTimeMillis() - startMillis);
                                startMillis = System.currentTimeMillis();
                                //等待超时时间
                                if (millisToWait > 0L) {
                                    this.wait(millisToWait);
                                } else {
                                    doDelete = true;//如果超时则删除锁
                                    break;
                                }
                            }
                        } catch (NoNodeException var19) {
                            ;
                        }
                    }
                }
            }
        } catch (Exception var21) {
            ThreadUtils.checkInterrupted(var21);
            doDelete = true;
            throw var21;
        } finally {
            if (doDelete) {
                //如果锁超时,删除锁
                this.deleteOurPath(ourPath);
            }
 
        }
 
        return haveTheLock;
    }

接着从StandardLockInternalsDriver的getsTheLock方法里面看,记得初始化的时候,maxLeases=1,children为有序的创建的临时节点,

    public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception {
        //得到当前节点的位置
        int ourIndex = children.indexOf(sequenceNodeName);
        validateOurIndex(sequenceNodeName, ourIndex);
        //当前节点是否<1,小于1说明为0,getsTheLock 为true,说明可以获得 锁
        boolean getsTheLock = ourIndex < maxLeases;
        //getsTheLock 为false,pathToWatch 为当前节点的上个节点,即监控节点
        String pathToWatch = getsTheLock ? null : (String)children.get(ourIndex - maxLeases);
        return new PredicateResults(pathToWatch, getsTheLock);
    }

还有一个细节可以看到

  //给当前节点的上个节点设置监控                     ((BackgroundPathable)this.client.getData().usingWatcher(this.watcher)).forPath(previousSequencePath);

看下watcher的方法

   private final Watcher watcher = new Watcher() {
        public void process(WatchedEvent event) {
            LockInternals.this.notifyFromWatcher();
        }
    };
  //唤醒等待的线程,也就是当节点发生变化的时候,会触发该事件,也就唤醒等待的线程
    private synchronized void notifyFromWatcher() {
        this.notifyAll();
    }

acquire获取锁的细节就分析到这里的,接下里来看release释放锁的逻辑

public void release() throws Exception {
        Thread currentThread = Thread.currentThread();
    //根据当前线程,获取缓存的数据
        InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
        //当前线程没有获取锁,不能释放
        if (lockData == null) {
            throw new IllegalMonitorStateException("You do not own the lock: " + this.basePath);
        } else {
            //锁释放,可重入锁的lockCount的数字减少1,lockCount的初始值为1
            int newLockCount = lockData.lockCount.decrementAndGet();
            if (newLockCount <= 0) {
                if (newLockCount < 0) {
                    throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + this.basePath);
                } else {//只有newLockCount  = 0才做这个逻辑,说明可重入锁全部释放
                    //释放锁
                    try {
                        //本质是删除节点
                        this.internals.releaseLock(lockData.lockPath);
                    } finally {  
                        //移除
                        this.threadData.remove(currentThread);
                    }
 
                }
            }
        }
    }

对应的releaseLock的方法,移除watcher和删除节点

  final void releaseLock(String lockPath) throws Exception {
        //移除watcher
        this.client.removeWatchers();
        this.revocable.set((Object)null);
      //删除节点
        this.deleteOurPath(lockPath);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值