zookeeper实现分布式锁

算法思路:使用临时顺序(EPHEMERAL_SEQUENTIAL)节点特性 

1,对于加锁操作,可以让所有客户端都去/lock目录下创建临时顺序节点,然后获取/lock节点的所有子节点,对所有子节点进行排序,

2,如果创建的客户端发现自身创建节点序列号是/lock/目录下最小的节点,则获得锁。否则,监视比自己创建节点的序列号小的节点(比自己创建的节点小的最大节点),

进入等待。

3,对于解锁操作,只需要将自身创建的节点删除即可

代码如下:

package com.cn.core;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

public class DistributeLockDemo implements Watcher {
	 
    ZooKeeper zk = null; //zookeeper原生api去实现一个分布式锁
 
    private String root = "/locks";
 
    private String myZonode; //表示当前获取到的锁名称-也就是节点名称
 
    private String waitNode; // 表示当前等待的节点
 
    private CountDownLatch latch;
 
    //zookeeper服务 url
    private static final String CONNECTION_STRING = "127.0.0.1:2181";
 
    private static final int SESSION_TIMEOUT = 10000; //超时时间
 
    /**
     * 构造函数初始化
     *
     * @param config 表示zookeeper连接串
     */
    public DistributeLockDemo(String config) {
        try {
            zk = new ZooKeeper(config, SESSION_TIMEOUT, this);
            Stat stat = zk.exists(root, false); //判断是不是已经存在locks节点,不需要监听root节点
            if (stat == null) { //如果不存在,则创建根节点
                zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
 
    public void process(WatchedEvent event) {
        if (this.latch != null) { //如果计数器不为空话话,释放计数器锁
            this.latch.countDown();
        }
    }
 
    /**
     * 获取锁的方法
     */
    public boolean lock(String name) {
        if (tryLock(name)) {
            System.out.println(Thread.currentThread().getName() +"-->"+ myZonode + " - 获取 lock!");
            return true;
        }
        try {
            return waitLock(waitNode, SESSION_TIMEOUT);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 释放锁操作的方法
     */
    public void unlock() {
        System.out.println(Thread.currentThread().getName()+"-->"+myZonode+"释放锁");
        try {
            zk.delete(myZonode, -1);
            myZonode = null;
            zk.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
 
    }
 
    private boolean tryLock(String name) {
        String splitStr = name; //lock_0000000001
        try {
            //创建一个有序的临时节点,赋值给myznode
            myZonode = zk.create(root + "/" + splitStr, new byte[0],
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println(Thread.currentThread().getName()+"-->"+myZonode + " 创建成功");
            List<String> subNodes = zk.getChildren(root, false);
            Collections.sort(subNodes); //讲所有的子节点排序
            if (myZonode.equals(root + "/" + subNodes.get(0))) {
                //当前客户端创建的临时有序节点是locks下节点中的最小的节点,表示当前的客户端能够获取到锁
                return true;
            }
            //否则的话,监听比自己小的节点 locks/lock_0000000003
            String subMyZnode = myZonode.substring((myZonode.lastIndexOf("/") + 1));
            waitNode = subNodes.get(Collections.binarySearch(subNodes, subMyZnode) - 1);// 获取比当前节点小的节点
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    private boolean waitLock(String lower, long waitTime) throws KeeperException, InterruptedException {
        Stat stat = zk.exists(root + "/" + lower, true); //获取节点状态,并添加监听
        if (stat != null) {
            System.out.println(Thread.currentThread().getName()+"-->"+ myZonode + " waiting for" + root + " /" + lower);
            this.latch = new CountDownLatch(1); //实例化计数器,让当前的线程等待
            this.latch.await(waitTime, TimeUnit.MILLISECONDS);
            this.latch = null;
        }
        System.out.println("Thread " + myZonode + "获取锁");
        return true;
    }
 
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        
      
//        final Semaphore semaphore = new Semaphore(10);
        for (int i = 0; i < 10; i++) {
        	Runnable runnable = new Runnable() {
				
				public void run() {
					try {
//	                    semaphore.acquire();
	                    DistributeLockDemo distributeLockDemo = new DistributeLockDemo(CONNECTION_STRING);
	                    boolean lock = distributeLockDemo.lock("product_");
	                    if (lock) {
	                    	 //业务代码
		                    System.out.println(Thread.currentThread().getName() +"-->"+"开始执行业务代码");
//		                    Thread.sleep(3000);
		                    distributeLockDemo.unlock();
						}
	                   
//	                    semaphore.release();
	                } catch (Exception e) {
	                    e.printStackTrace();
	                }
					
				}
			} ;
                
            
            executorService.execute(runnable);
        }
        executorService.shutdown();
 
 
    }
 
 
}



测试结果:

pool-1-thread-3-->/locks/product_0000000295 创建成功
pool-1-thread-6-->/locks/product_0000000296 创建成功
pool-1-thread-10-->/locks/product_0000000297 创建成功
pool-1-thread-5-->/locks/product_0000000298 创建成功
pool-1-thread-4-->/locks/product_0000000299 创建成功
pool-1-thread-7-->/locks/product_0000000300 创建成功
pool-1-thread-2-->/locks/product_0000000301 创建成功
pool-1-thread-8-->/locks/product_0000000304 创建成功
pool-1-thread-1-->/locks/product_0000000302 创建成功
pool-1-thread-9-->/locks/product_0000000303 创建成功
pool-1-thread-3-->/locks/product_0000000295 - 获取 lock!
pool-1-thread-3-->开始执行业务代码
pool-1-thread-3-->/locks/product_0000000295释放锁
pool-1-thread-4-->/locks/product_0000000299 waiting for/locks /product_0000000298
pool-1-thread-8-->/locks/product_0000000304 waiting for/locks /product_0000000303
pool-1-thread-2-->/locks/product_0000000301 waiting for/locks /product_0000000300
pool-1-thread-10-->/locks/product_0000000297 waiting for/locks /product_0000000296
pool-1-thread-7-->/locks/product_0000000300 waiting for/locks /product_0000000299
pool-1-thread-5-->/locks/product_0000000298 waiting for/locks /product_0000000297
pool-1-thread-6-->/locks/product_0000000296 waiting for/locks /product_0000000295
pool-1-thread-1-->/locks/product_0000000302 waiting for/locks /product_0000000301
pool-1-thread-9-->/locks/product_0000000303 waiting for/locks /product_0000000302
Thread /locks/product_0000000296获取锁
pool-1-thread-6-->开始执行业务代码
pool-1-thread-6-->/locks/product_0000000296释放锁
Thread /locks/product_0000000297获取锁
pool-1-thread-10-->开始执行业务代码
pool-1-thread-10-->/locks/product_0000000297释放锁
Thread /locks/product_0000000298获取锁
pool-1-thread-5-->开始执行业务代码
pool-1-thread-5-->/locks/product_0000000298释放锁
Thread /locks/product_0000000299获取锁
pool-1-thread-4-->开始执行业务代码
pool-1-thread-4-->/locks/product_0000000299释放锁
Thread /locks/product_0000000300获取锁
pool-1-thread-7-->开始执行业务代码
pool-1-thread-7-->/locks/product_0000000300释放锁
Thread /locks/product_0000000301获取锁
pool-1-thread-2-->开始执行业务代码
pool-1-thread-2-->/locks/product_0000000301释放锁
Thread /locks/product_0000000302获取锁
pool-1-thread-1-->开始执行业务代码
pool-1-thread-1-->/locks/product_0000000302释放锁
Thread /locks/product_0000000303获取锁
pool-1-thread-9-->开始执行业务代码
pool-1-thread-9-->/locks/product_0000000303释放锁
Thread /locks/product_0000000304获取锁
pool-1-thread-8-->开始执行业务代码
pool-1-thread-8-->/locks/product_0000000304释放锁



扩展:

menagerie
其实就是对以上思路的一个封装,不用自己写代码了。直接拿来用就可以了。
menagerie基于Zookeeper实现了java.util.concurrent包的一个分布式版本。这个封装是更大粒度上对各种分布式一致性使用场景的抽象。其中最基础和常用的是一个分布式锁的实现: org.menagerie.locks.ReentrantZkLock,通过ZooKeeper的全局有序的特性和EPHEMERAL_SEQUENTIAL类型znode的支持,实现了分布式锁。具体做法是:不同的client上每个试图获得锁的线程,都在相同的basepath下面创建一个EPHEMERAL_SEQUENTIAL的node。EPHEMERAL表示要创建的是临时znode,创建连接断开时会自动删除; SEQUENTIAL表示要自动在传入的path后面缀上一个自增的全局唯一后缀,作为最终的path。因此对不同的请求ZK会生成不同的后缀,并分别返回带了各自后缀的path给各个请求。因为ZK全局有序的特性,不管client请求怎样先后到达,在ZKServer端都会最终排好一个顺序,因此自增后缀最小的那个子节点,就对应第一个到达ZK的有效请求。然后client读取basepath下的所有子节点和ZK返回给自己的path进行比较,当发现自己创建的sequential node的后缀序号排在第一个时,就认为自己获得了锁;否则的话,就认为自己没有获得锁。这时肯定是有其他并发的并且是没有断开的client/线程先创建了node。
menagerie地址:https://github.com/openUtility/menagerie


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑的窝窝牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值