ZooKeeper入门(基于Zookeeper实现分布式锁),有代码

方式一:节点不可重名+watch

 缺点:惊群效应

(流程图)

 

这个功能是需要实现juc的Lock接口。

1、参数,构造器

        private String localpath;
	
	private ZkClient client ;
	
	public ZkDistributeLock(String localpath) {
		if(localpath== null || localpath.trim().equals("")) {
			throw new IllegalArgumentException("地址不能为空");
		}
		this.localpath = localpath;
		//创建Zkclient客户端对象
		client = new ZkClient("localhost:2181");
		//设置序列化
		client.setZkSerializer(new MyZkSerializer());
	}

2、重写tryLock方法

//尝试加锁,就是去创建节点
	public boolean tryLock() {
		
		try {
			client.createEphemeral(localpath);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

3、重写Lock方法

//所有线程争抢锁,如果没有获得锁,那么就要阻塞住
	public void lock() {
		if(!tryLock()) {
			waitForLock();
			//醒过来之后,继续进行获得锁操作
			lock();
		}

	}

4、具体阻塞方法,使用了watch,CountDownWatch

//阻塞住线程,只有该节点被删除才会触发--,此时锁被释放,需要一个watch,监听
	private void waitForLock() {
		final CountDownLatch cdl = new CountDownLatch(1);
		IZkDataListener listener = new IZkDataListener() {
			
			public void handleDataDeleted(String dataPath) throws Exception {
				//该节点被删除,有人释放了锁
				cdl.countDown();
				
			}
			
			public void handleDataChange(String dataPath, Object data) throws Exception {
				// TODO Auto-generated method stub
				
			}
		};
		//设置watch
		client.subscribeDataChanges(localpath, listener);
		
		try {	
			cdl.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//用完了(订阅)watch,就取消掉
		client.unsubscribeDataChanges(localpath, listener);
		
	}

5、重写UnLock

//删除节点,就是释放锁
	public void unlock() {
		client.delete(localpath);
	}

方式二:取号+最小化+watch

1、参数和构造器

private String localPath;//父节点
	private ZkClient client;
	//当前序号
	private ThreadLocal<String> currentPath = new ThreadLocal<String>();
	//排在我前面的序号
	private ThreadLocal<String> beforePath = new ThreadLocal<String>();
	
	
	
	public ZkDistributeImproveLock(String localPath) {
			if(localPath == null || localPath.trim().equals("")) {
				throw new IllegalArgumentException("地址不能为空");
			}
			this.localPath = localPath;
			client = new ZkClient("localhost:2181");
			client.setZkSerializer(new MyZkSerializer());
			//判断父节点是否存在,不存在则创建
			if(!this.client.exists(localPath)) {
				this.client.createPersistent(localPath, true);
			}
	}

2、重写tryLock()

public boolean tryLock() {
		//创建临时顺序子节点
		String node = client.createEphemeralSequential(localPath, "znode");
		//这就是当前的节点
		currentPath.set(node);
		//获取父节点下所有的子节点
		List<String> children = client.getChildren(localPath);
		//排序
		Collections.sort(children);
		
		//如何当前自己的是最小的,则第一个获取锁
		if(currentPath.get().equals(localPath+"/"+children.get(0))) {
			return true;
		}else {
			//获取当前节点在list里面的下标
			int curIndex = children.indexOf(currentPath.get().substring(localPath.length()+1));
			String beforeNode = localPath+"/"+children.get(curIndex-1);
			beforePath.set(beforeNode);
		}
		
		return false;
	}

3、重写Lock()

public void lock() {
		if(!tryLock()) {
			waitForLock();
			//唤醒,重新获取锁
			lock();
		}

	}
	private void waitForLock() {
		final CountDownLatch cdl = new CountDownLatch(1);
		IZkDataListener listener = new IZkDataListener() {
			
			public void handleDataDeleted(String dataPath) throws Exception {
				//该节点被删除,有人释放了锁
				cdl.countDown();
				
			}
			
			public void handleDataChange(String dataPath, Object data) throws Exception {
				// TODO Auto-generated method stub
				
			}
		};
		//设置watch
		client.subscribeDataChanges(beforePath.get(), listener);
		//如何前一个节点被删除了,则不去等待了
		if(this.client.exists(this.beforePath.get())) {
			try {	
				cdl.await();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//用完了(订阅)watch,就取消掉
		client.unsubscribeDataChanges(beforePath.get(), listener);
		
	}

4、重写UnLock()

public void unlock() {
		client.delete(currentPath.get());
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值