前言
        Curator是netflix公司开源的一套zookeeper客户端,目前是Apache的顶级项目。与Zookeeper提供的原生客户端相比,Curator的抽象层次更高,简化了Zookeeper客户端的开发量。Curator解决了很多zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册wathcer和NodeExistsException 异常等。

Curator主要解决了三类问题:

封装ZooKeeper client与ZooKeeper server之间的连接处理
提供了一套Fluent风格的操作API
提供ZooKeeper各种应用场景(recipe, 比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装,这些实现都遵循了zk的最佳实践,并考虑了各种极端情况

Curator由一系列的模块构成,对于一般开发者而言,常用的是curator-framework和curator-recipes: 

        curator-framework:提供了常见的zk相关的底层操作
        curator-recipes:提供了一些zk的典型使用场景的参考。本节重点关注的分布式锁就是该包提供的

1. 可重入锁InterProcessMutex 

 ReentrantJDKReentrantLock类似, 意味着同一个客户端在拥有锁的同时,可以多次获取,不会被阻塞。它是由类InterProcessMutex来实现。

// 常用构造方法
public InterProcessMutex(CuratorFramework client, String path)
// 获取锁
public void acquire();
// 带超时时间的可重入锁
public boolean acquire(long time, TimeUnit unit);
// 释放锁
public void release();
 

package com.cxb.java;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * @Classname ReentrantLockZk
 * @Date 2023/4/1 14:30
 * @Created by Administrator
 * @Description TODO
 */
public class ReentrantLockZk {

    public static final String ZOOKEEPER_STRING = "127.0.0.1:2181";

    CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(ZOOKEEPER_STRING, new ExponentialBackoffRetry(1000, 3));

    public ReentrantLockZk() {
        curatorFramework.start();
    }

    /**
     * 注意:如想重入,则需要使用同一个InterProcessMutex对象。
     */
    public void checkAndLock() {
        InterProcessMutex mutex = new InterProcessMutex(curatorFramework, "/curator/lock");
        try {
            // 加锁
            mutex.acquire();

            // 处理业务
            // 例如查询库存 扣减库存 模拟业务耗时
            Thread.sleep(1000);

            // 如想重入,则需要使用同一个InterProcessMutex对象
            this.testSub(mutex);

            // 释放锁
            mutex.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void testSub(InterProcessMutex mutex) {
        try {
            mutex.acquire();
            System.out.println("测试可重入锁。。。。");
            mutex.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ReentrantLockZk reentrantLockZk = new ReentrantLockZk();
        reentrantLockZk.checkAndLock();
    }
}

注意:如想重入,则需要使用同一个InterProcessMutex对象。 

2. 不可重入锁InterProcessSemaphoreMutex

具体实现:InterProcessSemaphoreMutexInterProcessMutex调用方法类似,区别在于该锁是不可重入的,在同一个线程中不可重入。

public InterProcessSemaphoreMutex(CuratorFramework client, String path);
public void acquire();
public boolean acquire(long time, TimeUnit unit);
public void release();

package com.cxb.java;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * @Classname NotReentrantSemaphoreMutex
 * @Date 2023/4/1 14:42
 * @Created by Administrator
 * @Description TODO
 */
public class NotReentrantSemaphoreMutex {
    public static final String ZOOKEEPER_STRING = "127.0.0.1:2181";

    public static CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(ZOOKEEPER_STRING, new ExponentialBackoffRetry(1000, 3));

    public NotReentrantSemaphoreMutex() {
        curatorFramework.start();
    }

    public void deduct() {
        InterProcessSemaphoreMutex mutex = new InterProcessSemaphoreMutex(curatorFramework, "/curator/lock");
        try {
            mutex.acquire();
            // 处理业务
            // 例如查询库存 扣减库存
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                mutex.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 该锁是不可重入锁
     * @param args
     */
    public static void main(String[] args) {
        InterProcessSemaphoreMutex mutex = new InterProcessSemaphoreMutex(curatorFramework, "/curator/lock");
        try {
            mutex.acquire();

            NotReentrantSemaphoreMutex notReentrantSemaphoreMutex = new NotReentrantSemaphoreMutex();
            notReentrantSemaphoreMutex.deduct();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                mutex.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

 从结果来看,确实不可重入。

3、可重入读写锁InterProcessReadWriteLock

 类似JDKReentrantReadWriteLock。一个拥有写锁的线程可重入读锁,但是读锁却不能进入写锁。这也意味着写锁可以降级成读锁。从读锁升级成写锁是不成的。主要实现类InterProcessReadWriteLock

// 构造方法
public InterProcessReadWriteLock(CuratorFramework client, String basePath);
// 获取读锁对象
InterProcessMutex readLock();
// 获取写锁对象
InterProcessMutex writeLock();

 注意:写锁在释放之前会一直阻塞请求线程,而读锁不会

package com.cxb.java;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.TimeUnit;

/**
 * @Classname ReadWriteLock
 * @Date 2023/4/1 14:49
 * @Created by Administrator
 * @Description TODO
 */
public class ReadWriteLock {

    public static final String ZOOKEEPER_STRING = "127.0.0.1:2181";

    public static CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(ZOOKEEPER_STRING, new ExponentialBackoffRetry(1000, 3));

    public ReadWriteLock() {
        curatorFramework.start();
    }

    public void testZkReadLock() {
        try {
            InterProcessReadWriteLock rwlock = new InterProcessReadWriteLock(curatorFramework, "/curator/rwlock");
            rwlock.readLock().acquire(10, TimeUnit.SECONDS);
            // TODO:一顿读的操作。。。。
            Thread.sleep(2000);
            System.out.println("正在读");
            rwlock.readLock().release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void testZkWriteLock() {
        try {
            InterProcessReadWriteLock rwlock = new InterProcessReadWriteLock(curatorFramework, "/curator/rwlock");
            rwlock.writeLock().acquire(10, TimeUnit.SECONDS);
            // TODO:一顿写的操作。。。。
            Thread.sleep(2000);
            System.out.println("正在写");
            rwlock.writeLock().release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ReadWriteLock readWriteLock = new ReadWriteLock();

        readWriteLock.testZkReadLock();

        readWriteLock.testZkWriteLock();
    }

}

 4、联锁InterProcessMultiLock

Multi Shared Lock是一个锁的容器。当调用acquire, 所有的锁都会被acquire,如果请求失败,所有的锁都会被release。同样调用release时所有的锁都被release(失败被忽略)。基本上,它就是组锁的代表,在它上面的请求释放操作都会传递给它包含的所有的锁。实现类InterProcessMultiLock:

// 构造函数需要包含的锁的集合,或者一组ZooKeeper的path
public InterProcessMultiLock(List<InterProcessLock> locks);
public InterProcessMultiLock(CuratorFramework client, List<String> paths);

// 获取锁
public void acquire();
public boolean acquire(long time, TimeUnit unit);

// 释放锁
public synchronized void release();

5、信号量InterProcessSemaphoreV2

一个计数的信号量类似JDK的Semaphore。JDK中Semaphore维护的一组许可(permits),而Cubator中称之为租约(Lease)。注意,所有的实例必须使用相同的numberOfLeases值。调用acquire会返回一个租约对象。客户端必须在finally中close这些租约对象,否则这些租约会丢失掉。但是,如果客户端session由于某种原因比如crash丢掉, 那么这些客户端持有的租约会自动close, 这样其它客户端可以继续使用这些租约。主要实现类InterProcessSemaphoreV2:

// 构造方法
public InterProcessSemaphoreV2(CuratorFramework client, String path, int maxLeases);

// 注意一次你可以请求多个租约,如果Semaphore当前的租约不够,则请求线程会被阻塞。
// 同时还提供了超时的重载方法
public Lease acquire();
public Collection<Lease> acquire(int qty);
public Lease acquire(long time, TimeUnit unit);
public Collection<Lease> acquire(int qty, long time, TimeUnit unit)

// 租约还可以通过下面的方式返还
public void returnAll(Collection<Lease> leases);
public void returnLease(Lease lease);

参考文章 

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐