ZK实现分布式锁的记录

实现分布式锁有很多种方式:

 1:数据库方式: 谁先插入数据库值成功, 谁就获取锁。

 2: 缓存方式

 3: zookeeper 实现分布式锁, 推荐这种方式

 

zk实现分布式锁的原理及流程: 临时节点 + 事件通知 + 信号量, 临时节点的生命周期是连接断开则会删除。

   zk有四种节点类型

    1: 持久化节点

    2:临时节点

    3:持久化顺序节点

    4: 临时顺序节点。

   zk 的事件通知 watcher.

实现分布式锁 流程:

  1: 连接zk

  2: 创建临时节点, 如果创建成功, 则获取锁, 代码执行完毕, 释放锁(也就是关闭zk连接)。

                               如果创建失败,则代表有服务创建了节点获取了锁, 此时则使用信号量等待。

 3: 节点删除的通知, 如果有节点被删除的通知过来了, 则唤醒信号量, 重新尝试获取锁。

代码:

1: 锁接口

/** 
 * ClassName: Lock
 * Function:  定义锁
 * date: 2019年7月10日 下午9:44:32
 * 
 * @author tangjiandong
 */
public interface Lock {
	
	void  getLock();
	
	void  unlock();

}

2: 锁的两个实现:

/** 
 * Project Name:distributed 
 * File Name:ZookeeperAbstractLock.java 
 * Package Name:com.tang.service 
 * Date:2019年7月10日 下午9:52:06 
 * Copyright (c) 2019,   All Rights Reserved. 
 * 
 */
package com.tang.service;

import java.util.concurrent.CountDownLatch;

import org.I0Itec.zkclient.ZkClient;

/** 
 * ClassName: ZookeeperAbstractLock
 * Function: TODO ADD FUNCTION. 
 * date: 2019年7月10日 下午9:52:06
 * 
 * @author tangjiandong
 */
public abstract  class ZookeeperAbstractLock implements Lock {

	private static final String  connectAddress = "127.0.0.1:2181"; //连接地址
	private static final int timeOut = 5000;   //超时时间
	protected static final String path = "/lock"; //创建的节点名称
	protected ZkClient zkClient = new ZkClient(connectAddress); //连接建立
	protected CountDownLatch countDownLatch = null; //信号量计数器
	
	
	/**
	 * 
	 * @see com.tang.service.Lock#getLock()
	 */
	@Override
	public void getLock()
	{
		   //是否获取锁
		   if(trylock())
		   {
			   System.out.println("---------获取锁!");
		   }else {
			   //如果等待到了锁,则继续获取锁, 否则继续等待
			   waitLock();
			   //尝试获取锁
			   getLock();
		   }
	}
    //是否获取锁
	abstract boolean trylock();
	
	//等待锁
	abstract void waitLock();
	/**
	 *关闭连接, 释放锁
	 * @see com.tang.service.Lock#unlock()
	 */
	@Override
	public void unlock() 
	{
		 if(null != zkClient)
		 {
			 zkClient.close();
			 System.out.println("---------释放锁!");
		 }
	}

}

 

/** 
 * Project Name:distributed 
 * File Name:ZookeeperDistributedLock.java 
 * Package Name:com.tang.service 
 * Date:2019年7月10日 下午10:10:23 
 * Copyright (c) 2019, All Rights Reserved. 
 * 
 */
package com.tang.service;

import java.util.concurrent.CountDownLatch;

import org.I0Itec.zkclient.IZkDataListener;

/** 
 * ClassName: ZookeeperDistributedLock
 * Function:   获取锁与等待锁的实现
 * date: 2019年7月10日 下午10:10:23
 * 
 * @author tangjiandong
 */
public class ZookeeperDistributedLock extends ZookeeperAbstractLock{

	/**
	 * 获取锁
	 * @return
	 * @see com.tang.service.ZookeeperAbstractLock#trylock()
	 */
	@Override
	boolean trylock() 
	{
		try {
			zkClient.createEphemeral(path); //创建临时节点
			return true; //创建成功则获取锁
		} catch (Exception e) {
			return false;  //创建失败则没有获取到锁, 需要等待
		}
	}

	/**
	 * 等待锁
	 * @see com.tang.service.ZookeeperAbstractLock#waitLock()
	 */
	@Override
	void waitLock()
	{
		//创建节点的监听
		IZkDataListener iZkDataListener = new IZkDataListener() {
			
			//节点被删除的通知
			@Override
			public void handleDataDeleted(String dataPath) throws Exception {
				if(null != countDownLatch) 
				{
					countDownLatch.countDown(); //计数器减一,唤醒等待
				}
			}
			
			//节点改变的通知
			@Override
			public void handleDataChange(String dataPath, Object data) throws Exception {
				// TODO Auto-generated method stub
				
			}
		};
		
		
		//注册监听
		try {
			zkClient.subscribeDataChanges(path, iZkDataListener);
		} catch (Exception e) {
			System.out.println("---------节点注册监听异常!");
		}

		
		
		if(zkClient.exists(path))  //如果节点存在了,则等待,
		{
			countDownLatch = new CountDownLatch(1);
			
			try {
				countDownLatch.await(); //可加上超时等待, 防止延迟通知导致死锁
                //countDownLatch.await(1000, TimeUnit.SECONDS);
			} catch (InterruptedException e) {
				//节点等待失败
				System.out.println("---------节点等待异常!");
				e.printStackTrace();
			}
		}
		
		//删除监听
		zkClient.unsubscribeDataChanges(path, iZkDataListener);
	}

}

 3:调用

/** 
 * Project Name:distributed 
 * File Name:CreateNumber.java 
 * Package Name:com.tang.number 
 * Date:2019��7��9�� ����8:38:45 
 * Copyright (c) 2019, ���쳤����Ϸֹ�˾  All Rights Reserved. 
 * 
 */
package com.tang.number;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.tang.service.ZookeeperDistributedLock;

/** 
 * ClassName: CreateNumber
 * Function: TODO ADD FUNCTION. 
 * date: 2019��7��9�� ����8:38:45
 * 
 * @author tangjiandong
 */
public class CreateNumber implements Runnable{

	private com.tang.service.Lock lock = new ZookeeperDistributedLock(); //创建锁
	
	/**
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		//synchronized (this) {
		lock.getLock();
		try {
			getNumber();
		} finally {
			lock.unlock();
		}
			
		//}
	}
	
	public void  getNumber()
	{
		String createJjdbh = JjdbhUtils.createJjdbh(null);
		System.out.println(createJjdbh);
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 100; i++) 
		{
			new Thread(  new CreateNumber()).start();
		}
		
	}
	

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值