ZooKeeper(七)-- ZK原生API实现分布式锁
一、使用场景
在分布式应用,往往存在多个进程提供同一服务。这些进程有可能在相同的机器上,也有可能分布在不同的机器上。 如果这些进程共享了一些资源,可能就需要分布式锁来锁定对这些资源的访问。
二、实现分布式锁结构图

三、代码实现
package com.xbq.zookeeper.javaApi; 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.Semaphore;
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.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat; /**
* 使用Zookeeper原生API实现分布式锁
* @author xbq
*/
public class ZookeeperLock implements Watcher{ // 声明zk对象
private ZooKeeper zk = null;
// 此demo使用的集群,所以有多个ip和端口
private static String CONNECT_SERVER = "192.168.242.130:2181,192.168.242.130:2182,192.168.242.130:2183";
// session过期时间
private static int SESSION_TIMEOUT = 3000;
// 根节点
private String root = "/locks";
// 当前等待的节点
private String waitNode;
// 等待的时间
private int waitTime;
// 锁节点
private String myzkNode;
// 计数器
private CountDownLatch latch; /**
* 构造函数 初始化
*/
public ZookeeperLock(){
try {
zk = new ZooKeeper(CONNECT_SERVER, SESSION_TIMEOUT, this);
// 判断是否存在根节点,不需要监听根节点
Stat stat = zk.exists(root, false);
// 为空,说明不存在
if(stat == null){
// 添加一个永久节点,即添加一个根节点
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 尝试获取锁
* @return
*/
private boolean tryLock(){
String splitStr = "lock_"; // 格式 lock_000000001
try {
// 创建一个临时有序节点,并赋值给 myzkNode
myzkNode = zk.create(root + "/" + splitStr, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// 获取根节点下的所有子节点
List<String> children = zk.getChildren(root, false);
// 对子节点 排序
Collections.sort(children);
// 如果刚刚创建的节点 等于 获取最小的一个子节点,则说明 获取到锁
if(myzkNode.equals(root + "/" + children.get(0))){
return true;
}
// 如果刚刚创建的节点 不等于 获取到的最小的一个子节点,则 监控比自己小的一个节点
// 获取刚刚建立的子节点(不包含根节点的子节点)
String childNode = myzkNode.substring(myzkNode.lastIndexOf("/") + 1);
// 获取比自己小的节点
waitNode = children.get(Collections.binarySearch(children, childNode) - 1);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
} /**
* 等待锁释放
* @param waitNode
* @param waidTime
* @return
* @throws KeeperException
* @throws InterruptedException
*/
private boolean waitLock(String waitNode, int waidTime) throws KeeperException, InterruptedException{
// 判断比自己小的节点是否存在,并添加监听
Stat stat = zk.exists(root + "/" + waitNode, true);
// 如果存在 比自己小的节点
if(stat != null){
this.latch = new CountDownLatch(1);
this.latch.await(waidTime, TimeUnit.MILLISECONDS);
this.latch = null;
}
return true;
} /**
* 获取锁
*/
public void lock(){
// 如果尝试获取锁成功
if(tryLock()){
// 获取 成功
System.out.println("Thread" + Thread.currentThread().getName() + " -- hold lock!");
return;
}
// 等待并获取锁
try {
waitLock(waitNode, waitTime);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
} /**
* 解锁
*/
public void unLock(){
try {
zk.delete(myzkNode, -1);
zk.close();
System.out.println("Thread" + Thread.currentThread().getName() +" unlock success! 锁节点:" + myzkNode);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
} /**
* 删除的时候 触发事件
*/
@Override
public void process(WatchedEvent event) {
// 如果计数器不为空的话,释放计数器锁
if(this.latch != null){
this.latch.countDown();
}
} /**
* 测试
* @param args
*/
public static void main(String[] args) {
// 定义线程池
ExecutorService service = Executors.newCachedThreadPool();
// 只能10个线程同时运行,即模拟并发数为10
final Semaphore semaphore = new Semaphore(10);
// 10个客户端连接
for(int i=0;i<10;i++){
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
ZookeeperLock zkLock = new ZookeeperLock();
zkLock.lock();
// 业务操作代码
Thread.sleep(3000);
zkLock.unLock();
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
源码下载:https://gitee.com/xbq168/DistributedLockByRedis
ZooKeeper(七)-- ZK原生API实现分布式锁的更多相关文章
- 使用ZooKeeper实现Java跨JVM的分布式锁(优化构思)
一.使用ZooKeeper实现Java跨JVM的分布式锁 二.使用ZooKeeper实现Java跨JVM的分布式锁(优化构思) 三.使用ZooKeeper实现Java跨JVM的分布式锁(读写锁) 说明 ...
- 使用ZooKeeper实现Java跨JVM的分布式锁(读写锁)
一.使用ZooKeeper实现Java跨JVM的分布式锁 二.使用ZooKeeper实现Java跨JVM的分布式锁(优化构思) 三.使用ZooKeeper实现Java跨JVM的分布式锁(读写锁) 读写 ...
- 使用ZooKeeper实现Java跨JVM的分布式锁
一.使用ZooKeeper实现Java跨JVM的分布式锁 二.使用ZooKeeper实现Java跨JVM的分布式锁(优化构思) 三.使用ZooKeeper实现Java跨JVM的分布式锁(读写锁) 说明 ...
- curator框架的使用以及实现分布式锁等应用与zkclient操作zookeeper,简化复杂原生API
打开zookeeper集群 先体会一下原生API有多麻烦(可略过): //地址 static final String ADDR = "192.168.171.128:2181,192.16 ...
- Zookeeper系列2 原生API 以及核心特性watcher
原生API 增删改查询 public class ZkBaseTest { static final String CONNECT_ADDR = "192.168.0.120"; ...
- ZooKeeper(八)-- Curator实现分布式锁
1.pom.xml <dependencies> <dependency> <groupId>junit</groupId> <artifactI ...
- Zookeeper系列三:Zookeeper客户端的使用(Zookeeper原生API如何进行调用、ZKClient、Curator)和Zookeeper会话
一.Zookeeper原生API如何进行调用 准备工作: 首先在新建一个maven项目ZK-Demo,然后在pom.xml里面引入zk的依赖 <dependency> <groupI ...
- ZooKeeper分布式锁的实现原理
七张图彻底讲清楚ZooKeeper分布式锁的实现原理[石杉的架构笔记] 文章转载自:https://juejin.im/post/5c01532ef265da61362232ed#comment(写的 ...
- ZooKeeper 分布式锁
在Redis分布式锁一文中, 作者介绍了如何使用Redis开发分布式锁. Redis分布式锁具有轻量高吞吐量的特点,但是一致性保证较弱.我们可以使用Zookeeper开发分布式锁,来满足对高一致性的要 ...
随机推荐
- gson 转换 List<Map> 注意事项
如果list泛型显示指定Map类型, 这时的Map 不能直接转换为 jre自带的 map类型 gson封装了 StringMap 进行转换
- C#与C++区别
C#是三大主流OOP(面向对象编程)语言(C++,Java,C#)之一,也是最新的一种,其中必然借鉴了前两者的长处,“否则它的缔造者就该打屁股”——候捷语,见<C#Primer中文版>的译 ...
- R 画structure图
id percent k1_B04_WL-1.fs_1 0.021 k31_B04_WL-1.fs_1 0.624 k21_B04_WL-1.fs_1 0.355 k1 K=3 数据输 ...
- 一款javascript实现的超炫的下拉选择框
今天为给大家带来一款javascript实现的超炫的下拉选择框.下拉的列表由半用透明的幽灵按钮组成.显示下拉的时候,列表项由左右两侧飞入.消息时飞向左右两侧.一起看下效果图 在线预览 源码下载 实 ...
- ExtJs 通过分析源代码解决动态加载Controller的问题
通过分析源代码解决动态加载Controller的问题 最近在研究ExtJs(4.2.0)的MVC开发模式,具体Extjs的MVC如何使用这里不解释,具体参见ExtJs的官方文档.这里要解决的问题是如何 ...
- 使用MultipartEntity上传文件(带进度对话框)
package com.home.uploadfile; import java.io.File; import android.app.Activity; import android.os.Bun ...
- SQL SERVER 2005快捷键
一.SQL SERVER 2005快捷键 快捷键 功能 CTRL + SHIFT + B生成解决方案 CTRL + F7 生成编译 CTRL + O 打开文件 CTRL + SHIFT + O打开项目 ...
- jquery 新建的元素事件绑定问题研究[转]
原文:http://www.cnblogs.com/linzheng/archive/2010/10/17/1853568.html js的事件监听跟css不一样,css只要设定好了样式,不论是原来就 ...
- (五)Qt实现自定义模型基于QAbstractItemModel
一个小例子 QTableView + QStandardItemModel QStandardItemModel model; //设置大小 model.setColumnCount(); //列 m ...
- USB2.0学习笔记连载(二):USB基础知识简介
USB接口分为USB A型.USB B型.USBmini型.USBmicro型.USB3.0其中每种都有相应的插座和插头. 图1 图2 上图是USBA型接口,图1为插座,图2为插头.插座指向下行方向, ...