实现思路

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

非公平锁:直接尝试在指定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. 解题:洛谷4178 Tree

    题面 重(新)学点分治中...... 普通的点分治一般这几步: 1.找重心 2.从重心开始DFS,得到信息 3.统计经过重心的路径 4.分别分治几棵子树,继续这个过程 然后是常见的(制杖的我的)一些疑 ...

  2. java中的date类型转换为js中的日期显示 我改

    function dateChange(javaDate){ if(javaDate){ return javaDate.substr(0,10).replace(/-/g,"/" ...

  3. 为什么Spring Boot推荐使用logback-spring.xml来替代logback.xml来配置logback日志的问题分析

    最根本的原因: 即,logback.xml加载早于application.properties,所以如果你在logback.xml使用了变量时,而恰好这个变量是写在application.proper ...

  4. JavaScript--Dom直接选择器

    一.简介 文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口.在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示 ...

  5. css基础--简单介绍css

    --引入 什么是css? CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素 样式通常存储在样式表中 把样式添加到 HTML 4.0 中,是为了解 ...

  6. 面试心得与总结---BAT、网易、蘑菇街

    作者:Xoper.ducky链接:https://www.nowcoder.com/discuss/3043来源:牛客网 之前实习的时候就想着写一篇面经,后来忙就给忘了,现在找完工作了,也是该静下心总 ...

  7. CS20 D LCA

    给出一棵树,许多询问,每次询问A,B,C三点,求一点使到三点距离最小,输出该点和最小值. 很明显就是求LCA,三种组合都求一次LCA,然后在里面选个距离和最小的就行了. 官方题解里面的代码求LCA是在 ...

  8. Nginx模块Lua-Nginx-Module学习笔记(一)Nginx Lua API 接口详解

    源码地址:https://github.com/Tinywan/Lua-Nginx-Redis 一.介绍 各种* _by_lua,* _by_lua_block和* _by_lua_file配置指令用 ...

  9. SpringCloud (十) Hystrix Dashboard单体监控、集群监控、与消息代理结合

    一.前言 Dashboard又称为仪表盘,是用来监控项目的执行情况的,本文旨在Dashboard的使用 分别为单体监控.集群监控.与消息代理结合. 代码请戳我的github 二.快速入门 新建一个Sp ...

  10. 20155213 2016-2017-2 《Java程序设计》第五周学习总结

    20155213 2016-2017-2 <Java程序设计>第五周学习总结 教材学习内容总结 Java中所有错误都会被打包为对象,运用try.catch,可以在错误发生时显示友好的错误信 ...