实现思路

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

非公平锁:直接尝试在指定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. bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机模板

    #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #d ...

  2. Java基础-面向对象第三大特性之多态(polymorphism )

    Java基础-面向对象第三大特性之多态(polymorphism) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.多态概述 多态是继封装,继承之后,面向对象的第三大特性,多态的 ...

  3. Windows 2012 R2 安装net4.6.1

    下载并安装Net4.6.1 根据提示下载如下,并安装 https://support.microsoft.com/zh-cn/help/2919355/windows-rt-8-1--windows- ...

  4. logstash过滤配置

    input { redis { host => "127.0.0.1" port => 6380 data_type => "list" ke ...

  5. Linux基础命令【记录】

    后台运行详情:https://www.cnblogs.com/little-ant/p/3952424.html 查看端口.查找等命令 根据关键字查找文件信息: cat <文件名> | g ...

  6. 组合数学及其应用——polya计数

    在处理类似下面的问题中,一般的计数方法会出现问题:假如你要用红.蓝两种颜色给一个正四面体的四个顶点着色,试问存在多少种不同的着色方案? 在高中我们常用的方法是模拟涂色过程,分情况讨论,然后基于分步乘法 ...

  7. 二分算法的应用——最大化最小值 POJ2456 Aggressive cows

    Aggressive cows Time Limit: 1000MS Memory Limit: 65536K Total Submissions: Accepted: Description Far ...

  8. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第五部分(Page 10)

    编写你的第一个 Django app,第五部分(Page 10)转载请注明链接地址 我们继续建设我们的 Web-poll 应用,本节我们会为它创建一些自动测试. 介绍自动测试 什么是自动测试 测试是简 ...

  9. 经典幻灯片插件Swiper

    照着写的demo,搞清楚什么叫分页器Pagination,什么叫nav,搞清楚DOM结构,container,wrapper之类的,就能写了.效果掉渣天! <!DOCTYPE html> ...

  10. 网络爬虫框架Heritrix中Modules的各项说明

    1)Select Crawl Scope:Crawl Scope 用于配置当前应该在什么范围内抓取网页链接.例如选择 BroadScope 则表示当前的抓取范围不受限制,选择 HostScope 则表 ...