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学习笔记【十】 画一个简单的三角形
本篇笔记要实现的是在屏幕上渲染出一个三角形,重点要学习的是渲染一个几何体的流程方式. 为了渲染几何图形,需要一个顶点缓存和一个描述顶点布局的输入层,还有着色器(主要是顶点着色器和像素着色器),下面来看 ...
随机推荐
- Fiddler高级用法
Fiddler高级用法 1. 简单用法 Fiddler作为一个基于http协议的抓包工具,一直在业界有广泛使用.很多测试或者前端在使用Fiddler时,仅仅用于查看前端和服务端之间的请求信息.包括我作 ...
- composer 遇见问题 整理
修改镜像源:改为阿里云镜像 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 报错:Con ...
- CSS3中Animation为同一个元素添加多个动画效果
CSS3 Animation 并未提供 给一个元素同时添加多个动画效果的方法,就是说一个元素,只能给它定义一个动画效果,不能同时定义. 需求说明比如说,我想实现一个这样的动画效果: 一颗星星从上往下滑 ...
- POJ 2976 01分数规划基础题目
题意: 给你一组"数",一共n个,每个数有两个权值,价钱a[i],代价b[i],让你选择n - k使得 sigma(a[i]) / sigma(b[i]) * 100 ...
- 重新封装了一下NODE-MONGO 使其成为一个独立的服务.可以直接通过get/post来操作
# 重新封装了一下NODE-MONGO 使其成为一个独立的服务.可以直接通过get/post来操作 # consts.js 配置用的数据,用于全局参数配置 # log.js 自己写的一个简单的存储本地 ...
- Intel汇编语言程序设计学习-第一章 基本概念
第一章基本概念 1.1 简单介绍 本书着重讲述MS-Windows平台上IA-32(Intel Architecture 32bit,英特尔32位体系架构)兼容微处理器的汇编语言程序设计,可以使用I ...
- Linux-鸟菜-6-文件与目录的 默认权限、隐藏权、特殊权限
Linux-鸟菜-6-文件与目录的 默认权限.隐藏权.特殊权限 除了基本r,w,x权限外,在Linux还可以设定其他系统隐藏属性,可以用chattr来设定,和lsattr来查看,但注意一点,CentO ...
- XCTF-i-get-id-200
i-get-id-200 题目描述 嗯..我刚建好了一个网站 解题过程 一共有三个页面 Hello World 告诉了页面是perl写的 Forms 输入name和age会返回渲染后的字符串 搜了一下 ...
- golang:协程安全
多路复用 Go语言中提供了一个关键字select,通过select可以监听channel上的数据流动.select的用法与switch语法类似,由select开始一个新的选择块,每个选择条件由case ...
- Linux查看登录日志 last命令 查看当前登录用户
Linux查看登录日志 linux 发布于 31 分钟前 lastlog 打印系统账号最近一次的登录记录情况,解析的是/var/log/lastlog文件,它是一个data file类型的文件,文 ...