zookeeper实现分布式锁

闲来无事,用Java的zk客户端Curator实现了一个简易的分布式可重入锁,这里的可重入指的是线程可重入,如果有需求是应用可重入只需要稍加修改,将获取锁的状态记录即可,例如最简单的记到static字段,那么一个部署实例就可以共享一个锁状态。另外,如果没有而外的需求,还是直接使用Curator-recipes项目的InterProcessMutex作为分布式锁比较好,毕竟Apache项目现成的轮子。
实现的思路在网上有很多,Zookeeper实现分布式锁 里的流程图画得很好,或者可以直接看InterProcessMutex的实现思路,总体来说有点类似AQS,把等待锁的候选人排成一个队列,从前往后赋予资源。

构造参数

public DistributedLock(CuratorFramework client, String basePath) {
    this.client = client;
    this.basePath = basePath;
}

client从外部注入由外部决定连接的细节,锁内部只需要client提供的api。basePath是锁路径,用同一个锁路径初始化分布式锁,当使用的是同一个zk客户端时,不同的DistributedLock实例也能有互斥的效果(同一个app不同线程、不同app)。

lock

public boolean tryLock(long time, TimeUnit unit) {
    //当前线程正在持有锁
    ThreadContext threadContext = threadLocal.get();
    if (threadContext != null) {
        threadContext.lockCount++;
        return true;
    }
    //等待时间的处理
    long startTime = System.currentTimeMillis();
    long waitTime = unit.toMillis(time);
    boolean getLock = false;
    //生成候选人结点
    try {
        String path = ZKPaths.makePath(basePath, LOCK_NAME);
        selfPath = client
                .create()
                .creatingParentsIfNeeded()
                .withProtection()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                .forPath(path);
        log.info("lock self path {}", selfPath);
    } catch (Exception exception) {
        deleteSelf();
        throw new RuntimeException(exception);
    }
    while (true) {
        try {
            //获取所有的候选人结点
            List children = client.getChildren().forPath(basePath);
            List sortedList = Lists.newArrayList(children);
            Collections.sort(sortedList);
            String selfNode = ZKPaths.getNodeFromPath(selfPath);
            //获取当前候选人所在的位置
            int selfIndex = sortedList.indexOf(selfNode);
            if (selfIndex < 0) {
                throw new IllegalMonitorStateException();
            }
            //如果是第一个结点,则获得锁
            if (selfIndex == 0) {
                getLock = true;
                break;
            }
            //监控前一个结点,当前一个结点释放锁时,当前候选人获取锁
            synchronized (this) {
                String previousSequencePath = ZKPaths.makePath(basePath, sortedList.get(selfIndex - 1));
                log.info("watch {}", previousSequencePath);
                client.getData().usingWatcher(watcher).forPath(previousSequencePath);
                if (waitTime == 0) {
                    break;
                } else if (waitTime > 0) {
                    long diff = System.currentTimeMillis() - startTime;
                    if (diff < waitTime) {
                        wait(waitTime - diff);
                    } else {
                        deleteSelf();
                        break;
                    }
                } else {
                    wait();
                    log.info("wake up");
                }
            }
        } catch (Exception e) {
            deleteSelf();
            throw new RuntimeException(e);
        }
    }
    //记录线程获取锁的上下文
    if (getLock) {
        threadLocal.set(new ThreadContext(1));
    }
    return getLock;
}

unlock

public void unlock() {
    ThreadContext context = threadLocal.get();
    if (context == null) {
        throw new IllegalMonitorStateException("Thread not have lock");
    }
    if (context.lockCount == 1) {
        deleteSelf();
        threadLocal.set(null);
    } else {
        context.lockCount--;
    }
}

详细代码见:分布式锁

你可能感兴趣的:(zookeeper实现分布式锁)