zookeeper应用之分布式锁

在分布式系统中多个服务需要竞争同一个资源时就需要分布式锁,这里使用zookeeper的临时顺序节点来实现分布式锁。

在节点X下创建临时顺序节点,getChildren()获取节点X的所有子节点,判断当前节点是否是第一个子节点,如果是就获取锁成功了,如果不是,那么就监听当前节点的前一个节点删除watcher事件,在前一节点删除之前当前线程需要阻塞等待,前一节点删除在watcher事件处理通知当前线程获取锁成功。

实现一:

还是使用curator按照上面的逻辑先来自己实现一个简易版的

首先需要抽象出一个DistributeLock类,有两个操作获取锁和释放锁。剩下的就是存储一些加锁节点路径信息等。

锁类定义如下:

java 复制代码
public class DistributeLock {
    private CuratorFramework client;
    private String ROOT_PATH = "/test_lock";
    //顺序节点的父节点
    private String lockpath;
    //当前创建顺序节点的路径(全路径)
    private String currPath;
    //线程等待latch
    private CountDownLatch latch = new CountDownLatch(1);

    public DistributeLock(CuratorFramework client,String lockpath){
        this.client = client;
        this.lockpath = lockpath;
    }

    public boolean lock(){
        try {
            //创建临时顺序节点
            currPath = client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(ROOT_PATH+"/"+lockpath);
            String lockId = currPath.substring(currPath.lastIndexOf("/")+1);
            List<String> children = client.getChildren().forPath(ROOT_PATH);
            Collections.sort(children);
            //当前节点是顺序节点中第一个,获锁成功
            if(currPath.endsWith(children.get(0))){
                System.out.println(Thread.currentThread().getName() +"get lock0");
                return true;
            }
            /**
             * 不是第一个,监听排在自己前面节点的删除事件
             */
            //获取当前顺序节点前一节点的索引
            int preIndex = Collections.binarySearch(children,lockId)-1;
            //前一节点是否存在
            Stat stat = client.checkExists().forPath(ROOT_PATH+"/"+children.get(preIndex));
            System.out.println(ROOT_PATH+"/"+children.get(preIndex));
            System.out.println(stat==null);
            if(stat != null){//设置节点监听
                CuratorCache cache = CuratorCache.build(client,ROOT_PATH+"/"+ children.get(preIndex));
                cache.listenable().addListener(new CuratorCacheListener() {
                    @Override
                    public void event(Type type, ChildData childData, ChildData childData1) {
                        if(Type.NODE_DELETED.equals(type))
                            latch.countDown();
                    }
                });
                cache.start();
            }else{
                return true;
            }
            //等到前节点删除事件发生
            latch.await();
            System.out.println(Thread.currentThread().getName() +"get lock3");
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    public void unlock(){
        try {//删除当前节点
            client.delete().forPath(currPath);
            System.out.println(Thread.currentThread().getName() +"release lock");
            client.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这里使用了CountDownLatch来进行线程阻塞,只是用来实现逻辑,很多细节没有考虑。比如可重入异常控制等。

然后使用这个分布式锁来控制线程执行:

java 复制代码
//定义一个竞争资源
private final static AtomicInteger stock = new AtomicInteger(10);
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
    executor.submit(new Runnable() {
        @Override
        public void run() {
            CuratorFramework client = CuratorFrameworkFactory
                    .newClient("localhost:2181", new ExponentialBackoffRetry(1000, 3));
            client.start();
            DistributeLock lock = new DistributeLock(client,"stock");
            lock.lock();
            int value = stock.decrementAndGet();
            System.out.println("stock change to:"+value);
            try {
                Thread.sleep(new Random().nextInt(2000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
        }
    });
}
executor.shutdown();

定义一个竞争资源stock,多个线程对该资源进行操作,一次只允许一个线程进行操作。

实现二:

在curator的recipes包里同样提供了工具类InterProcessMutex用来获取互斥锁。

java 复制代码
//初始化锁,需要client连接和锁路径参数
InterProcessMutex mutexLock = new InterProcessMutex(client,"/mutex_lock");
//获取锁 阻塞等待,有重构方法可以设置等待时间
mutexLock.acquire();
//do sth
//释放锁
mutexLock.release();

他这里的锁就是可重入锁。一个线程acquire要对应同等量的release。

相关推荐
Francek Chen3 小时前
【大数据技术基础 | 实验十二】Hive实验:Hive分区
大数据·数据仓库·hive·hadoop·分布式
吾日三省吾码3 小时前
JVM 性能调优
java
弗拉唐4 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi775 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3435 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀5 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20205 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深5 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
shuangrenlong6 小时前
slice介绍slice查看器
java·ubuntu