ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁
作者:Grey
原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁
前置知识
完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用
需求
当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
在单机情况下,可以使用JUC包里面的工具来进行互斥控制。
但是在分布式系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁的由来。
当多个进程不在同一个系统中,就需要用分布式锁控制多个进程对资源的访问。
我们可以用ZooKeeper来模拟实现一个简单的分布式锁
环境准备
一个zk集权,ip和端口分别为:
- 192.168.205.145:2181
- 192.168.205.146:2181
- 192.168.205.147:2181
- 192.168.205.148:2181
定义主方法
App.java
public class App {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
ZkLock lock = new ZkLock();
lock.lock(); // 开启锁
System.out.println(Thread.currentThread().getName() + " doing work");
lock.release(); // 释放锁
}).start();
}
while (true) {
}
}
}
如上,我们设计了一个ZkLock,其中lock方法是锁定资源,release方法是释放资源,我们并发了10个线程并发访问来模拟。
public class ZkLock implements AsyncCallback.StringCallback, Watcher, AsyncCallback.StatCallback, AsyncCallback.Children2Callback {
private CountDownLatch latch;
private ZooKeeper zk;
private String identify;
private String lockPath;
private String pathName;
public ZkLock() {
identify = Thread.currentThread().getName();
lockPath = "/lock";
latch = new CountDownLatch(1);
zk = ZookeeperConfig.create(ADDRESS + "/testLock");
}
public void lock() {
try {
zk.create(lockPath, currentThread().getName().getBytes(UTF_8), OPEN_ACL_UNSAFE, EPHEMERAL_SEQUENTIAL, this, currentThread().getName());
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void release() {
try {
zk.delete(pathName, -1);
System.out.println(identify + " over work....");
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
}
@Override
public void processResult(int rc, String path, Object ctx, String name) {
if (null != name) {
// 创建成功
System.out.println(identify + " created " + name);
pathName = name;
zk.getChildren("/", false, this, "dasdfas");
}
}
@Override
public void processResult(int rc, String path, Object ctx, List<String> children, Stat stat) {
sort(children);
int i = children.indexOf(pathName.substring(1));
if (i == 0) {
// 是第一个,获得锁,可以执行
System.out.println(identify + " first...");
try {
zk.setData("/", identify.getBytes(UTF_8), -1);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
} else {
zk.exists("/" + children.get(i - 1), this, this, "ddsdf");
}
}
@Override
public void process(WatchedEvent event) {
switch (event.getType()) {
case None:
break;
case NodeCreated:
break;
case NodeDeleted:
zk.getChildren("/", false, this, "sdf");
break;
case NodeDataChanged:
break;
case NodeChildrenChanged:
break;
}
}
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
}
}
关于上述代码的说明,我们规定创建的zk目录为/testLock,所以我们可以通过zk客户端在集群中先把/testLock目录建好,后续线程争抢的时候,我们只需要创建序列化的临时节点(以/lock开头),因为是序列化的,所以我们可以设置让第一个创建好节点的线程抢到锁,其他的线程排队等待。
所以lock方法实现如下:
zk.create(lockPath, currentThread().getName().getBytes(UTF_8), OPEN_ACL_UNSAFE, EPHEMERAL_SEQUENTIAL, this, currentThread().getName());
lock方法在执行的时候,会有一个回调,即:当节点创建成功后,会判断/testLock节点中有没有已经创建好的且在当前节点之前的节点,有的话,则注册一个一个对于/testLock目录的监听:
@Override
public void processResult(int rc, String path, Object ctx, String name) {
if (null != name) {
// 创建成功
System.out.println(identify + " created " + name);
pathName = name;
zk.getChildren("/", false, this, "dasdfas");
}
}
一旦发现/testLock目录下已经有节点了,那么我们拿到/testLock下的所有节点,并排序,取最小的那个节点执行即可:
@Override
public void processResult(int rc, String path, Object ctx, List<String> children, Stat stat) {
sort(children);
int i = children.indexOf(pathName.substring(1));
if (i == 0) {
// 是第一个,获得锁,可以执行
System.out.println(identify + " first...");
try {
zk.setData("/", identify.getBytes(UTF_8), -1);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
} else {
zk.exists("/" + children.get(i - 1), this, this, "ddsdf");
}
}
release方法很简单,只需要把当前执行完毕的节点删除即可:
public void release() {
try {
zk.delete(pathName, -1);
System.out.println(identify + " over work....");
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
}
运行效果
确保zk中有/testLock这个节点,如果没有,请先创建一个:
Run App.java
可以看到控制台输出:
Thread-5 created /lock0000000000
Thread-4 created /lock0000000001
Thread-1 created /lock0000000002
Thread-9 created /lock0000000003
Thread-6 created /lock0000000004
Thread-2 created /lock0000000005
Thread-3 created /lock0000000006
Thread-0 created /lock0000000007
Thread-8 created /lock0000000008
Thread-7 created /lock0000000009
Thread-5 first...
Thread-5 doing work
Thread-5 over work....
Thread-4 first...
Thread-4 doing work
Thread-4 over work....
Thread-1 first...
Thread-1 doing work
Thread-1 over work....
Thread-9 first...
Thread-9 doing work
Thread-9 over work....
Thread-6 first...
Thread-6 doing work
Thread-6 over work....
Thread-2 first...
Thread-2 doing work
Thread-2 over work....
Thread-3 first...
Thread-3 doing work
Thread-3 over work....
Thread-0 first...
Thread-0 doing work
Thread-0 over work....
Thread-8 first...
Thread-8 doing work
Thread-8 over work....
Thread-7 first...
Thread-7 doing work
Thread-7 over work....
ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁的更多相关文章
- ensorflow学习笔记四:mnist实例--用简单的神经网络来训练和测试
http://www.cnblogs.com/denny402/p/5852983.html ensorflow学习笔记四:mnist实例--用简单的神经网络来训练和测试 刚开始学习tf时,我们从 ...
- QML学习笔记(五)— 做一个简单的待做事项列表
做一个简单的QML待做事项列表,能够动态添加和删除和编辑数据 GitHub:八至 作者:狐狸家的鱼 本文链接:QML学习笔记(五)— 做一个待做事项列表 主要用到QML:ListView 效果 全部代 ...
- 【WPF】学习笔记(一)——做一个简单的电子签名板
参加实习(WPF)已经有两个多周的时间了,踩了一些坑,也算积累了一些小东西,准备慢慢拿出来分享一下.(●'◡'●) 这次呢就讲讲一个简单的电子签名板的实现. 先上张图(PS:字写得比较丑,不要太在意哈 ...
- Directx11学习笔记【十三】 实现一个简单地形
本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5510294.html 上一个教程我们实现了渲染一个会旋转的立方体, ...
- Directx11学习笔记【十一】 画一个简单的三角形--effect框架的使用
这里不再介绍effect框架的具体使用,有关effect框架使用可参考http://www.cnblogs.com/zhangbaochong/p/5475961.html 实现的功能依然是画一个简单 ...
- 【转】Redis学习笔记(五)如何用Redis实现分布式锁(2)—— 集群版
原文地址:http://bridgeforyou.cn/2018/09/02/Redis-Dsitributed-Lock-2/ 单机版实现的局限性 在上一篇文章中,我们讨论了Redis分布式锁的实现 ...
- Redis学习笔记(三)使用Lua脚本实现分布式锁
Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行. 使用Lua脚本的好处如下: 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放 ...
- 使用redis设计一个简单的分布式锁
最近看了有关redis的一些东西,了解了redis的一下命令,就记录一下: redis中的setnx命令: 关于redis的操作命令,我们一般会使用set,get等一系列操作,数据结构也有很多,这里我 ...
- Directx11学习笔记【十】 画一个简单的三角形
本篇笔记要实现的是在屏幕上渲染出一个三角形,重点要学习的是渲染一个几何体的流程方式. 为了渲染几何图形,需要一个顶点缓存和一个描述顶点布局的输入层,还有着色器(主要是顶点着色器和像素着色器),下面来看 ...
随机推荐
- 技术分享|SQL和 NoSQL数据库之间的差异:MySQL(VS)MongoDB
在当今市场上,存在各种类型的数据库,选择适合你业务类型的数据库对应用的开发和维护有着重要意义.本篇文章,将为大家分享SQL和NoSQL语言之间的区别,同时还将比较这两种类型的数据库,以帮助小伙伴们选择 ...
- ThnikPHP3.2 学习链接整理
ThnikPHP3.2 学习链接整理 ThinkPHP3.2.3 U()方法的使用总结 看云手册 ThinkPHP3.2完全开发手册 TP3.2单字母函数 TP3.x中 M方法和D方法的区别
- 手机改 user模式为debug模式
logcat 是Android中一个命令行工具,可用于监控手机应用程序的log信息.网上相关的教学很多,这里只想把自己折腾 2 部手机(一个是三星S4 I9500 港水,Android 5.01,一个 ...
- UVA10020(最小区间覆盖)
题意: 给你一个区间[0,m]和一些小的区间[l,r]让你选择最少的小区间个数去把整个区间覆盖起来. 思路: 算是比较经典的贪心题目吧(经典于难度没什么对应关系),大体思路可以 ...
- hdu3182 状态压缩dp
题意: 一个人做汉堡包,每个汉堡包有自己的花费和价值,某些汉堡包必须是在其他的某些汉堡包已经做好了的前提下才能制作,给你这个人的初始钱数,问最大的价值是多少. 思路: 比较简单 ...
- Linux下逻辑卷LVM的管理和RAID磁盘阵列
目录 LVM 一:LVM的创建 二:LVM的拉伸 三:LVM的缩小 四:LVM的删除 五:RAID磁盘阵列的添加 LVM LVM(Logical Volume Manager) 逻辑卷管理器,可以动态 ...
- 路由选择协议(RIP/OSPF)
目录 IGP RIP协议 OSPF协议 IS-IS协议 EIGRP协议 EGP BGP 我们可能会想,在偌大的网络中,我们是如何跟其他人通信的呢?我们是如何跟远在太平洋对面的美国小伙伴对话的呢? 这就 ...
- Win64 驱动内核编程-21.DKOM隐藏和保护进程
DKOM隐藏和保护进程 主要就是操作链表,以及修改节点内容. DKOM 隐藏进程和保护进程的本质是操作 EPROCESS 结构体,不同的系统用的时候注意查下相关定义,确定下偏移,下面的数据是以win7 ...
- android 代码中使用textAppearance
一开始在代码中我以为使用tvAge.setTextAppearance(context, resid);这样的的方式就能行, 运行之后发现这个设置并未生效,于是到处搜索在代码中设置系统样式的的解决方法 ...
- helium的浏览器启动及option配置 - 1
helium的浏览器启动及option配置 前言 helium只支持chrome和firefox两个浏览器,其中option配置是基于selelium来配置的,所以所调用的也是seleium的配置方式 ...