zk curator-recipes分布式锁InterProcessMutex详解

public class Lock {

    public static void main(String[] args) {
        CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new RetryOneTime(1000));
        client.start();

        InterProcessMutex lock = new InterProcessMutex(client, "/d-lock");

        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println("尝试获取锁。。。");
                try {
                    lock.acquire();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("获得锁");

                try {
                    System.out.println("执行中。。。。。。。。。。");
                    Thread.sleep(1000 * 60 * 10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行完,释放锁");
                try {
                    lock.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("释放成功");
            }).start();
        }
    }
}

查看lock.acquire()方法, acquire支持同线程重入,获取不到锁会一直阻塞。

    /**
     * Acquire the mutex - blocking until it's available. Note: the same thread
     * can call acquire re-entrantly. Each call to acquire must be balanced by a call
     * to {@link #release()}
     *
     * @throws Exception ZK errors, connection interruptions
     */
    @Override
    public void acquire() throws Exception
    {
        if ( !internalLock(-1, null) )
        {
            throw new IOException("Lost connection while trying to acquire lock: " + basePath);
        }
    }

查看internallock方法,处理重入并获取锁。

    private boolean internalLock(long time, TimeUnit unit) throws Exception
    {
       
        Thread currentThread = Thread.currentThread();

        LockData lockData = threadData.get(currentThread);
        if ( lockData != null )
        {
            // 重入
            lockData.lockCount.incrementAndGet();
            return true;
        }
        //尝试获取锁
        String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
        if ( lockPath != null )
        {
            LockData newLockData = new LockData(currentThread, lockPath);
            threadData.put(currentThread, newLockData);
            return true;
        }

        return false;
    }

查看internals.attempLock方法

    String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception
    {
        //记录当前时间
        final long      startMillis = System.currentTimeMillis();
        //记录锁等待时间
        final Long      millisToWait = (unit != null) ? unit.toMillis(time) : null;
        //临时顺序节点的data
        final byte[]    localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes;
        //节点不存在重试次数
        int             retryCount = 0;
        //临时顺序节点path
        String          ourPath = null;
        //当前path是否获取到锁
        boolean         hasTheLock = false;
        //flag
        boolean         isDone = false;
        while ( !isDone )
        {
            isDone = true;

            try
            {
                //创建临时顺序节点
                ourPath = driver.createsTheLock(client, path, localLockNodeBytes);
                //获取锁
                hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);
            }
            catch ( KeeperException.NoNodeException e )
            {
                // gets thrown by StandardLockInternalsDriver when it can't find the lock node
                // this can happen when the session expires, etc. So, if the retry allows, just try it all again
                if ( client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) )
                {
                    isDone = false;
                }
                else
                {
                    throw e;
                }
            }
        }
        //获取到锁,返回path
        if ( hasTheLock )
        {
            return ourPath;
        }

        return null;
    }

查看createsTheLock方法

    @Override
    public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception
    {
        String ourPath;
        if ( lockNodeBytes != null )
        {
            ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);
        }
        else
        {
            ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);
        }
        return ourPath;
    }

查看internalLockLoop方法

    private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception
    {
        //没有锁
        boolean     haveTheLock = false;
        //不删除
        boolean     doDelete = false;
        try
        {
            if ( revocable.get() != null )
            {
                client.getData().usingWatcher(revocableWatcher).forPath(ourPath);
            }
            //自旋,直到获取到锁
            while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock )
            {
                //获取到所有的子节点并排序
                List<String>        children = getSortedChildren();
                //当前节点名称
                String              sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash
                //判断是否可以拿到锁,前一个path:当前path是否获取到锁
                PredicateResults    predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);
                //如果拿到锁,返回
                if ( predicateResults.getsTheLock() )
                {
                    haveTheLock = true;
                }
                else
                {
                    //前一个path
                    String  previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();

                    synchronized(this)
                    {
                        try 
                        {
                            // use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak
                        //增加watcher,实现就是notifyAll,如果节点不存在抛出节点不存在异常    
                         client.getData().usingWatcher(watcher).forPath(previousSequencePath);
                            //如果有等待时间
                            if ( millisToWait != null )
                            {
                                millisToWait -= (System.currentTimeMillis() - startMillis);
                                startMillis = System.currentTimeMillis();
                                if ( millisToWait <= 0 )
                                {
                                    doDelete = true;    // timed out - delete our node
                                    break;
                                }

                                wait(millisToWait);
                            }
                            else
                            {
                                //无限等待
                                wait();
                            }
                        }
                        catch ( KeeperException.NoNodeException e ) 
                        {
                            // it has been deleted (i.e. lock released). Try to acquire again
                        }
                    }
                }
            }
        }
        catch ( Exception e )
        {
            doDelete = true;
            throw e;
        }
        finally
        {
            if ( doDelete )
            {
                deleteOurPath(ourPath);
            }
        }
        return haveTheLock;
    }
发布了44 篇原创文章 · 获赞 13 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_19457117/article/details/83017351