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学习笔记【十】 画一个简单的三角形
本篇笔记要实现的是在屏幕上渲染出一个三角形,重点要学习的是渲染一个几何体的流程方式. 为了渲染几何图形,需要一个顶点缓存和一个描述顶点布局的输入层,还有着色器(主要是顶点着色器和像素着色器),下面来看 ...
随机推荐
- Python数据类型之字符串类型
字符串的表示 字符串是Python中最常用的数据类型之一,必须使用成对的引号包围来表示字符串,引号可以是单引号 ' .双引号 " .三引号''' """,格式如 ...
- 1-OSI七层模型
网络功能:数据传输 ISO(国际标准化组织) OSI七层模型---->网络通信工作流程的标准化 OSI七层模型 应用层:提供用户服务,具体功能由特定的程序而定. 表示层:数据的压缩优化,加密. ...
- php的call_user_func_array()使用场景
1..动态调用普通函数时,比如参数和调用方法名称不确定的时候很好用 function sayEnglish($fName, $content) { echo 'I am ' . $content; } ...
- DVWA之SQL Injection
SQL Injection SQL Injection,即SQL注入,是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的.SQL注入漏洞的危害是巨大的,常常 ...
- C#-string生成图片
public static Bitmap GetLink(string Wordstr) { #region older //arial //StrForImg sf = new StrForImg( ...
- Windows核心编程 第八章 用户方式中线程的同步(上)
第8章 用户方式中线程的同步 当所有的线程在互相之间不需要进行通信的情况下就能够顺利地运行时, M i c r o s o f t Wi n d o w s的运行性能最好.但是,线程很少能够在所有的时 ...
- 远程连接mysql出现"Can't connect to MySQL server 'Ip' ()"的解决办法
1.大多是防火墙的问题(参考链接:https://blog.csdn.net/jiezhi2013/article/details/50603366) 2.上面方法不能解决,不造成影响情况下可关闭防火 ...
- mitmproxy 获取请求响应数据
比较好的一个介绍是:https://blog.wolfogre.com/posts/usage-of-mitmproxy/ mitproxy 获取请求响应数据的代码如下: # -*- coding: ...
- Office·Word高级·VBA基础概念语法
阅文时长 | 5.21分钟 字数统计 | 1823字符 『Office·Word高级·VBA基础概念语法』 编写人 | SCscHero 编写时间 | Monday, June 29, 2020 文章 ...
- .NET平台系列9 .NET Core 3.0 / .NET Core 3.1 详解
系列目录 [已更新最新开发文章,点击查看详细] .NET Core 3.0 于 2019年9月23日发布,重点是增加对同时支持使用 Windwos Forms.WPF 和 Entity Frm ...