zookeeper分布式锁原理及项目应用
https://www.cnblogs.com/linjiqin/p/6052031.html
pom.xml
1 2 3 4 5 | <dependency> <groupId>com.101tec< /groupId > <artifactId>zkclient< /artifactId > <version>0.11< /version > < /dependency > |
logback.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?xml version= "1.0" ?> <configuration> <!-- ch.qos.logback.core.ConsoleAppender 控制台输出 --> <appender name= "console" class= "ch.qos.logback.core.ConsoleAppender" > <encoder charset= "UTF-8" > <pattern>[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n< /pattern > < /encoder > < /appender > <!-- 日志级别 --> <root> <level value= "info" /> <appender-ref ref= "console" /> < /root > < /configuration > |
GenerateOrderNoService
1 2 3 4 5 6 7 8 | public class GenerateOrderNoService { static int count = 0; public static int getOrderNo() { count++; return count; } } |
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class OrderService implements Runnable { ZookeeperLock lock = new ZookeeperLock(); @Override public void run() { int orderNo = getOrderNo(); System.out.println(Thread.currentThread().getName() + " 测试 " + " : " + orderNo); } private int getOrderNo() { try { lock.lock(); int orderNo = 0; for (int i = 1; i <= 1000; i++) { orderNo = GenerateOrderNoService.getOrderNo(); } return orderNo; } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } return 0; } public static void main(String[] args) throws InterruptedException { OrderService orderService = new OrderService(); for (int i = 1; i <= 5; i++) { Thread thread = new Thread(orderService); thread.start(); // Thread. sleep (200); } } } |
ZookeeperLock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; /** * 持久化根节点: /lock * 最小节点才能获取锁 * 非最小节点 watch 监听上一个节点,监听是否删除操作,删除才获取锁,否则阻塞等待 * 获取锁,执行完毕业务,需要释放锁,释放锁即删除临时节点 */ public class ZookeeperLock { private String host = "192.168.239.131:2181,192.168.239.132:2181,192.168.239.133:2181,192.168.239.104:134" ; private ZkClient zkClient; private String persistentNodePath = "/lock" ; private ThreadLocal<String> curTl = new ThreadLocal<>(); // 当前节点 /lock/t0001 private ThreadLocal<String> beforeTl = new ThreadLocal<>(); // 前个节点 /lock/t0002 lastZoneId - zoneId = 1 public ZookeeperLock() { zkClient = new ZkClient(host); if (!zkClient.exists(persistentNodePath)) { zkClient.createPersistent(persistentNodePath, "" ); } } /** * 尝试获取锁 * * 最小节点 : 得到锁。 * 非最小节点 : 等待锁。 * * @ return true :得到锁, false :未得到锁 */ private boolean tryLock() { // 创建当前临时节点 String currentEphemeralZoneId = curTl.get(); if (currentEphemeralZoneId == null) { currentEphemeralZoneId = zkClient.createEphemeralSequential(persistentNodePath + "/" , null); System.out.println(Thread.currentThread() + " 创建临时节点 " + currentEphemeralZoneId); curTl. set (currentEphemeralZoneId); } // 获取 /lock 下所有子节点 List<String> children = zkClient.getChildren(persistentNodePath); // 排序 Collections. sort (children); // 获取当前节点 String first = children.stream().findFirst().get(); // 是否为最小节点 if (currentEphemeralZoneId.equals(persistentNodePath + "/" + first)) { return true ; } else { // 获取上一个节点 int curIndex = children.indexOf(currentEphemeralZoneId.substring(persistentNodePath.length() + 1)); String beforeEphemeralZoneId = persistentNodePath + "/" + children.get(curIndex - 1); beforeTl. set (beforeEphemeralZoneId); System.out.println(Thread.currentThread() + " 当前:" + currentEphemeralZoneId + ", 前个:" + beforeEphemeralZoneId); return false ; } } /** * 等待锁 : 未获取到锁 则阻塞并等待锁 * 注意:要先注册监听器, * 如果后注册监听器,会导致监听器未启动,线程便进入await,await等不到countDownLatch.countDown();就会一直处于等待状态,陷入死锁。 * * @ return */ private void waitForLock() throws InterruptedException { String beforeEphemeralZoneId = beforeTl.get(); String currentEphemeralZoneId = curTl.get(); // 线程计数器 CountDownLatch countDownLatch = new CountDownLatch(1); // 构造监听器 IZkDataListener listener = new IZkDataListener() { @Override public void handleDataChange(String dataPath, Object data) throws Exception { System.out.println( "节点改变" ); } @Override public void handleDataDeleted(String dataPath) throws Exception { System.out.println(Thread.currentThread() + " 监听到" + beforeEphemeralZoneId + "被删除, 唤醒" + currentEphemeralZoneId); // 删除上个节点, 唤醒当前线程 countDownLatch.countDown(); } }; // 开启监听器 zkClient.subscribeDataChanges(beforeEphemeralZoneId, listener); // 二次确认:是否存在前个节点 if (zkClient.exists(beforeEphemeralZoneId)) { // 存在:阻塞当前线程 System.out.println(Thread.currentThread() + " " + currentEphemeralZoneId + " 进等待了! " ); countDownLatch.await(); System.out.println(currentEphemeralZoneId + " 拿到锁了!" ); } else { // 不存在:关闭监听器 zkClient.unsubscribeDataChanges(beforeEphemeralZoneId, listener); } } /** * 获取锁 * @ return */ public void lock() throws Exception { if (!tryLock()) { waitForLock(); lock(); } } /** * 释放锁 * @ return */ public void unlock() { String currentEphemeralZoneId = curTl.get(); System.out.println( "释放锁,删除节点 " + currentEphemeralZoneId); zkClient.delete(currentEphemeralZoneId); curTl.remove(); } } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析