zookeeper应用之分布式锁Java实现

原文地址:http://blog.csdn.net/z69183787/article/details/79355829

zookeeper在项目中有很多应用,其中一个比较常见的的就是分布式锁,zookeeper实现分布式锁的原理是根据zookeeper创建的临时有序节点,每次zookeeper在同一个目录下创建的临时有序节点是有序的,会自动累加,如果本次操作创建的节点在目录内是最小节点,则获得锁,否则阻塞等待锁,并且总是在前一个节点上注册watcher监视前一个节点的释放,较小的节点释放后,等待的节点对应的操作获得锁,以此类推,代码简单实现如下:

public class ZKDisLock {
    private static final Logger SERVICE_LOG = LoggerFactory.getLogger(LogConstants.SERVICE_LOG);
    //锁目录
    private static final String BASE_PATH = "/disLock";
    private static final String SPLIT_FLAG = "_";
    private Integer sessionOut;
    private String zkHost;
    private ZooKeeper zooKeeper;
    //当前锁节点名称
    private String lockNode;
    //等待锁节点名称
    private String waitNode;
    private CountDownLatch countDownLatch;
 
    public ZKDisLock(Integer sessionOut, String zkHost) {
        this.sessionOut = sessionOut;
        this.zkHost = zkHost;
    }
 
    //ZKDisLock初始化
    public void init() throws DisLockException {
        try {
            zooKeeper = new ZooKeeper(zkHost, sessionOut, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                }
            });
            //判断有无根目录,没有的话创建
            Stat stat = zooKeeper.exists(BASE_PATH, false);
            if (stat == null) {
                zooKeeper.create(BASE_PATH, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        catch (Exception e) {
            SERVICE_LOG.info("zk初始化失败");
            throw new DisLockException("zk初始化失败");
        }
    }
 
    //加锁
    public void lock(String content) throws DisLockException {
        if (zooKeeper == null) {
            throw new DisLockException("zk未初始化");
        }
        try {
            //创建节点
            String itemName = zooKeeper.create(BASE_PATH + "/" + content + SPLIT_FLAG, null,
                                               ZooDefs.Ids.OPEN_ACL_UNSAFE, 
                                               CreateMode.EPHEMERAL_SEQUENTIAL);
            //判断节点是否是目录中同一个content的最小节点
            if (isLowestNode(content, itemName)) {
                SERVICE_LOG.info("线程:" + Thread.currentThread().getId() + ",加锁成功,节点名称:" + lockNode);
                return;
            else {
                SERVICE_LOG.info("线程:" + Thread.currentThread().getId() + ",等待锁:" + waitNode);
                //不是最小节点,等待锁释放
                waitForLock(sessionOut);
 
                //锁释放后,再次判断下是否是最小节点
                if (isLowestNode(content, itemName)) {
                    SERVICE_LOG.info("线程:" + Thread.currentThread().getId() + ",获得锁,节点名称:" + lockNode);
                    return;
                }
            }
        catch (Exception e) {
            SERVICE_LOG.error("加锁异常");
            throw new DisLockException("加锁异常");
        }
    }
 
    //判断是否是最小节点
    private boolean isLowestNode(String content, String itemName) throws Exception {     
        //获取所有子节点
        List<String> nodeNames = zooKeeper.getChildren(BASE_PATH, null);
        List<String> itemNodes = new ArrayList<>();
        lockNode = itemName;
        //过滤content节点
        for (String name : nodeNames) {
            if (name.split(SPLIT_FLAG)[0].toString().equals(content)) {
                itemNodes.add(name);
            }
        }
        //排序
        Collections.sort(itemNodes);
 
        //最小节点返回
        if (itemName.equals(BASE_PATH + "/" + itemNodes.get(0))) {
            return true;
        }
        //非最小节点,找到前一个节点
        int nodeIndex = Collections.binarySearch(itemNodes, itemName.substring(itemName.lastIndexOf("/") + 1));
        waitNode = itemNodes.get(nodeIndex - 1);
        return false;
    }
 
    //等待锁
    private void waitForLock(final Integer waitTime) throws Exception {
        //判断等待节点是否释放,同时注册watcher,通知释放事件
        Stat stat = zooKeeper.exists(BASE_PATH + "/" + waitNode, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                SERVICE_LOG.info("watch proceess " + watchedEvent.toString());
                if (countDownLatch != null) {
                    //锁释放,通知等待节点
                    countDownLatch.countDown();
                }
            }
        });
        //如果等待的锁不存在返回
        if (stat == null) {
            return;
        }
        //存在,等待
        countDownLatch = new CountDownLatch(1);
        countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
    }
 
    //释放锁
    public void unLock() throws DisLockException {
        try {
            //删除锁节点
            zooKeeper.delete(lockNode, -1);
            SERVICE_LOG.info("线程:" + Thread.currentThread().getId() + ",解锁成功,节点名称:" + lockNode);
            lockNode = null;
            zooKeeper.close();
        catch (Exception e) {
            String errorMsg = "解锁异常,节点名称:" + lockNode;
            SERVICE_LOG.error(errorMsg);
            throw new DisLockException(errorMsg);
        }
 
    }
 
}

 

以上是用zookeeper实现分布式锁的一个简单实现,可能有些地方并不太严谨,但是足以说明问题,下面来测试下这个分布式锁:

public static void main(String[] args) throws  Exception{
    ExecutorService executorService= Executors.newFixedThreadPool(40);
    String content="abcdefg";
    String zkHost="127.0.0.1";
    Integer sessionOut=30000;
    //创建10个线程测试
    for(int i=0;i<10;++i){
        ZKDisLock disLock=new ZKDisLock(sessionOut,zkHost);
        disLock.init();
        executorService.submit(new TestJob(disLock,content));
    }
 
}
 
//测试任务
private static class TestJob implements Runnable{
    private ZKDisLock disLock;
    private String content;
    public TestJob(ZKDisLock disLock, String content) {
        this.disLock = disLock;
        this.content = content;
    }
    @Override
    public void run() {
        try {
            disLock.lock(content);
            Thread.sleep(2000);
            disLock.unLock();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
 
    }
}

运行结果如下:

17-12-20 15:03:03,436 INFO  service(ZKDisLock.java:73) ## 线程:13,等待锁:abcdefg_0000000327
17-12-20 15:03:03,436 INFO  service(ZKDisLock.java:73) ## 线程:19,等待锁:abcdefg_0000000328
17-12-20 15:03:03,436 INFO  service(ZKDisLock.java:70) ## 线程:16,加锁成功,节点名称:/disLock/abcdefg_0000000327
17-12-20 15:03:03,438 INFO  service(ZKDisLock.java:73) ## 线程:22,等待锁:abcdefg_0000000329
17-12-20 15:03:03,441 INFO  service(ZKDisLock.java:73) ## 线程:25,等待锁:abcdefg_0000000330
17-12-20 15:03:03,444 INFO  service(ZKDisLock.java:73) ## 线程:28,等待锁:abcdefg_0000000331
17-12-20 15:03:03,456 INFO  service(ZKDisLock.java:73) ## 线程:31,等待锁:abcdefg_0000000332
17-12-20 15:03:03,460 INFO  service(ZKDisLock.java:73) ## 线程:34,等待锁:abcdefg_0000000333
17-12-20 15:03:03,464 INFO  service(ZKDisLock.java:73) ## 线程:37,等待锁:abcdefg_0000000334
17-12-20 15:03:03,469 INFO  service(ZKDisLock.java:73) ## 线程:40,等待锁:abcdefg_0000000335
17-12-20 15:03:05,442 INFO  service(ZKDisLock.java:137) ## 线程:16,解锁成功,节点名称:/disLock/abcdefg_0000000327
17-12-20 15:03:05,443 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000327
17-12-20 15:03:05,444 INFO  service(ZKDisLock.java:76) ## 线程:13,获得锁,节点名称:/disLock/abcdefg_0000000328
17-12-20 15:03:07,449 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000328
17-12-20 15:03:07,449 INFO  service(ZKDisLock.java:137) ## 线程:13,解锁成功,节点名称:/disLock/abcdefg_0000000328
17-12-20 15:03:07,451 INFO  service(ZKDisLock.java:76) ## 线程:19,获得锁,节点名称:/disLock/abcdefg_0000000329
17-12-20 15:03:09,459 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000329
17-12-20 15:03:09,459 INFO  service(ZKDisLock.java:137) ## 线程:19,解锁成功,节点名称:/disLock/abcdefg_0000000329
17-12-20 15:03:09,461 INFO  service(ZKDisLock.java:76) ## 线程:22,获得锁,节点名称:/disLock/abcdefg_0000000330
17-12-20 15:03:11,469 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000330
17-12-20 15:03:11,469 INFO  service(ZKDisLock.java:137) ## 线程:22,解锁成功,节点名称:/disLock/abcdefg_0000000330
17-12-20 15:03:11,471 INFO  service(ZKDisLock.java:76) ## 线程:25,获得锁,节点名称:/disLock/abcdefg_0000000331
17-12-20 15:03:13,480 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000331
17-12-20 15:03:13,480 INFO  service(ZKDisLock.java:137) ## 线程:25,解锁成功,节点名称:/disLock/abcdefg_0000000331
17-12-20 15:03:13,482 INFO  service(ZKDisLock.java:76) ## 线程:28,获得锁,节点名称:/disLock/abcdefg_0000000332
17-12-20 15:03:15,488 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000332
17-12-20 15:03:15,488 INFO  service(ZKDisLock.java:137) ## 线程:28,解锁成功,节点名称:/disLock/abcdefg_0000000332
17-12-20 15:03:15,490 INFO  service(ZKDisLock.java:76) ## 线程:31,获得锁,节点名称:/disLock/abcdefg_0000000333
17-12-20 15:03:17,498 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000333
17-12-20 15:03:17,498 INFO  service(ZKDisLock.java:137) ## 线程:31,解锁成功,节点名称:/disLock/abcdefg_0000000333
17-12-20 15:03:17,500 INFO  service(ZKDisLock.java:76) ## 线程:34,获得锁,节点名称:/disLock/abcdefg_0000000334
17-12-20 15:03:19,506 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000334
17-12-20 15:03:19,506 INFO  service(ZKDisLock.java:137) ## 线程:34,解锁成功,节点名称:/disLock/abcdefg_0000000334
17-12-20 15:03:19,508 INFO  service(ZKDisLock.java:76) ## 线程:37,获得锁,节点名称:/disLock/abcdefg_0000000335
17-12-20 15:03:21,516 INFO  service(ZKDisLock.java:118) ## watch proceess WatchedEvent state:SyncConnected type:NodeDeleted 
path:/disLock/abcdefg_0000000335
17-12-20 15:03:21,516 INFO  service(ZKDisLock.java:137) ## 线程:37,解锁成功,节点名称:/disLock/abcdefg_0000000335
17-12-20 15:03:21,518 INFO  service(ZKDisLock.java:76) ## 线程:40,获得锁,节点名称:/disLock/abcdefg_0000000336
17-12-20 15:03:23,527 INFO  service(ZKDisLock.java:137) ## 线程:40,解锁成功,节点名称:/disLock/abcdefg_0000000336

 从以上结果可以看到,线程16最先获得锁,创建最小节点 /disLock/abcdefg_0000000327,其次是线程13等待线程16的锁释放,其对应节点是/disLock/abcdefg_0000000328,等待abcdefg_0000000327释放,线程16锁释放后,线程13获得锁,其次是线程19等在线程13之后,以此类推,最后所有线程都获得了锁并且成功解锁,另外上述运行结果也展示了线程之间的锁的交接时通过注册的watcher传递的。以上的测试结果表明,我们实现的分布式锁可以正确的运行,达到了预期效果。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值