实现思路

公平锁:创建有序节点,判断本节点是不是序号最小的节点(第一个节点),若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件。

非公平锁:直接尝试在指定path下创建节点,创建成功,则说明该节点抢到锁了。如果创建失败,则监听锁节点的删除事件,或者sleep一段时间后再重试。

可重入:使用ThreadLocal记录加锁客户端的唯一标识。重复时先从ThreadLocal获取,获取到,这认为加锁成功,直接返回。

使用瞬时节点创建可重入公平锁

使用瞬时节点的好处是当Session失效时,该节点将被清理,从而避免使用持久节点加锁成功后,抛异常或宕机或服务器重启等原因造成的锁无法释放问题。

使用curator实现

// 假设需要加锁的订单Id
private static String orderId = "157146671409578219";
// 工程名
private static String appName = "trade_";
// 此次加锁业务处理逻辑描述
private static String operatorDesc = "updateTrade_"; // 部门,每个部门可以有自己的zk空间目录
private static String department = "zfpt" ; // 锁前缀 应该根据业务 具有唯一性
private static String lockPrefixKey = "/" + appName + operatorDesc ; /**
* 每个线程 创建自己的Connection ,创建自己的Session
*/
@Test
public void curator() throws Exception { for (int i = 0 ; i < 100 ; i++) { Thread currentThread = new Thread(() -> { // 创建Connection
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("master:2181,slave1:2181,slave2:2181")
.retryPolicy(new RetryOneTime(1000)) //重试策略
.namespace(department) // 可以设置自己部门缩写
.build();
client.start(); // 模拟对同一个订单加锁
InterProcessMutex lock = new InterProcessMutex(client, lockPrefixKey + orderId); try {
// 一直尝试加锁 直到锁可用。 有点像synchronized
// lock.acquire();
if(lock.acquire(1, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + " 抢到锁");
} else {
System.out.println(Thread.currentThread().getName() + "超时没有抢到锁");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 如果当前线程获得到锁,则释放锁
if(lock.isAcquiredInThisProcess()) {
System.out.println(Thread.currentThread().getName() + " 释放锁");
lock.release();
} else {
System.out.println(Thread.currentThread().getName() + " 没有抢到锁,故没有释放锁");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
currentThread.setName("Lock【" + i + "】");
currentThread.start();
} Thread.sleep(Integer.MAX_VALUE);
}

实现原理:

因为ZK不允许在临时节点下创建子节点,所以InterProcessMutex工具类会根据加锁传入path的(也就是案例中的lockPrefixKey + orderId)创建一个持久节点;然后在这个持久节点下创建瞬时节点,创建成功后,对该持久节点下的全部子节点进行降序排序,判断当前节点是否是第一个节点,如果是则获取锁,否则对前一个节点加上监听事件。然后Object.wait(),当监听事件被触发,则会调用notifyAll方法,对等待线程进行唤醒。再次尝试获取锁。

缺点:

  1. 传入的加锁节点会被创建为永久节点(就是lockPrefixKey + orderId),这样zk节点数量将会急速递增。
  2. 临时节点不稳定:一个客户端加锁成功后,可能会因为网络抖动等原因导致Session断开,该客户端创建的临时节点被清理,导致另外一节正在监听的客户端加锁成功,同时进行操作。

使用持久节点创建可重入公平锁

上文提到的临时节点不稳定,父节点为永久节点无法释放问题。我的拙见是:

  1. 使用持久节点代替临时节点:释放锁的时候删除自己创建的加锁节点。
  2. 父节点为永久节点无法释放:可以在每个客户端释放锁的时候进行判断,如果自己是最后一个节点,则删除父节点。
  3. 但是需要考虑的问题:客户端加锁成功后,宕机或重启或者其他极端异常情况,无法删除加锁节点。最后一个加锁节点同样异常,也无法删除父节点。这时,可以给每个锁加过期时间,过期失效。由于zk没有提供过期自动清理,所以可以在第二次访问该节点的时候 先进行判断,判断失效先删除再创建。如果没有第二次访问的节点 可以依靠定时任务进行节点清理。

ZK分布式锁(未完 待续)的更多相关文章

  1. Go web编程学习笔记——未完待续

    1. 1).GOPATH设置 先设置自己的GOPATH,可以在本机中运行$PATH进行查看: userdeMacBook-Pro:~ user$ $GOPATH -bash: /Users/user/ ...

  2. 本地锁、redis分布式锁、zk分布式锁

    本地锁.redis分布式锁.zk分布式锁 https://www.cnblogs.com/yjq-code/p/dotnetlock.html 为什么要用锁? 大型站点在高并发的情况下,为了保持数据最 ...

  3. 【分布式锁的演化】终章!手撸ZK分布式锁!

    前言 这应该是分布式锁演化的最后一个章节了,相信很多小伙伴们看完这个章节之后在应对高并发的情况下,如何保证线程安全心里肯定也会有谱了.在实际的项目中也可以参考一下老猫的github上的例子,当然代码没 ...

  4. git安装与使用,未完待续... ...

    ​ 目录 一.git概念 二.git简史 三.git的安装 四.git结构 五.代码托管中心-本地库和远程库的交互方式 六.初始化本地仓库 七.git常用命令 1.add和commit命令 2.sta ...

  5. javascript有用小功能总结(未完待续)

    1)javascript让页面标题滚动效果 代码如下: <title>您好,欢迎访问我的博客</title> <script type="text/javasc ...

  6. ASP.NET MVC 系列随笔汇总[未完待续……]

    ASP.NET MVC 系列随笔汇总[未完待续……] 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的. 学前篇之: ASP.NET MVC学前篇之扩展方法.链式编程 ASP. ...

  7. 关于DOM的一些总结(未完待续......)

    DOM 实例1:购物车实例(数量,小计和总计的变化) 这里主要是如何获取页面元素的节点: document.getElementById("...") cocument.query ...

  8. 我的SQL总结---未完待续

    我的SQL总结---未完待续 版权声明:本文为博主原创文章,未经博主允许不得转载. 总结: 主要的SQL 语句: 数据操作(select, insert, delete, update) 访问控制(g ...

  9. virtualbox搭建ubuntu server nginx+mysql+tomcat web服务器1 (未完待续)

    virtualbox搭建ubuntu server nginx+mysql+tomcat web服务器1 (未完待续) 第一次接触到 linux,不知道linux的确很强大,然后用virtualbox ...

  10. MVC丶 (未完待续······)

         希望你看了此小随 可以实现自己的MVC框架     也祝所有的程序员身体健康一切安好                                                     ...

随机推荐

  1. [THUSC 2016] 补退选 (Trie树)

    link $solution:$ $Trie$树很显然吧,那么如何去处理每次询问.对于$Trie$树的每个节点放一个$vector$表示其若有$v$个人的最小时间. #include<iostr ...

  2. cloudera manager安装hive注意事项,提示连不上数据库,没有user目录权限

    1.提示连不上数据库,password:null 解决方法:拷贝数据库驱动到hive的lib目录,数据库要使用安装hive机器的本地数据库,远程的可能连不上 2.没有/user目录权限 解决方法:因为 ...

  3. UnityShader 序列帧动画效果

    实现原理:主要思想是设置显示uv纹理的大小,并逐帧修改图片的uv坐标. 实现步骤 1.我们首先用_Time.y和速度属性_Speed相乘得到模拟的时间. 2.然后我们用time除以_Horizonta ...

  4. bzoj 2243

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 8800  Solved: 3305[Submit][Status ...

  5. STL中的优先级队列priority_queue

    priority_queue(queue类似)完全以底部容器为根据,再加上二叉堆(大根堆或者小根堆)的实现原理,所以其实现非常简单,缺省情况下priority_queue以vector作为底部容器.另 ...

  6. Ansible4:Ad-hoc与命令执行模块

    目录 Ad-hoc 命令说明 后台执行 命令执行模块 command模块 script模块 Ad-Hoc 是指ansible下临时执行的一条命令,并且不需要保存的命令,对于复杂的命令会使用playbo ...

  7. python中的关键字符

    from keyword import kwlistprint(kwlist)for i in kwlist: print(i) 可以显示所有的关键字符,开发者不要重新赋予其他值. a = 10000 ...

  8. 用rem做响应式开发

    设置对应的响应式的html rem比例 rem就是根元素(即:html)的字体大小.html中的所有标签样式凡是涉及到尺寸的(如: height,width,padding,margin,font-s ...

  9. MAC下 Apache服务器配置

    今天做了一个注册登录提交的页面,后续操作需要用到后端的知识 php+Mysql,之前只是有些了解,现在开始具体操作了,首先从配置环境开始.查了好几篇文档与博客,了解了挺多知识. Mac下Apache服 ...

  10. 何凯文每日一句打卡||DAY1~DAY3

    01长难句 In an open meeting with congressional Democrats and Republicans, Trump embraced raising the ag ...