使用Zookeeper实现分布式锁
利用Zookeeper临时节点(客户端异常断开连接后临时节点自动移除)或者Redis SETNX(set if not exists)(设置ttl)可以实现分布式锁,这里先利用zk实现一个
1.启动zk
2.代码实现
2.1 Maven引入zk & zk client
2.2 代码和注释
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 | import org.I0Itec.zkclient.ZkClient; import java.util.concurrent.CountDownLatch; public class ZKDistributeLockTest { public static void main(String[] args) { // 使用CountDownLunch控制线程同时执行 CountDownLatch countDownLatch = new CountDownLatch( 1 ); // 开启3个线程模拟分布式环境,分布式环境下每个进程都是一个单独的zkClient Thread t1 = new Thread( new TestThread(countDownLatch)); Thread t2 = new Thread( new TestThread(countDownLatch)); Thread t3 = new Thread( new TestThread(countDownLatch)); t1.start(); t2.start(); t3.start(); System.out.println( "休眠1秒后执行..." + System.currentTimeMillis()); try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } // 倒计时结束 countDownLatch.countDown(); } } // 线程,尝试在zk上创建临时节点,创建成功则获得锁(执行权) class TestThread implements Runnable { // 共享变量 private static Integer CNT = 0 ; private ZkClient zkClient; private CountDownLatch countDownLatch; public TestThread(CountDownLatch countDownLatch) { this .countDownLatch = countDownLatch; } // 连接zk private void connect() { String threadName = Thread.currentThread().getName(); try { System.out.println(threadName + " 等待执行..." ); // 等待倒计时结束 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(threadName + " 请求连接zk..." + System.currentTimeMillis()); zkClient = new ZkClient( "192.168.1.217:2181" , 20000 ); System.out.println(threadName + " 连接成功..." ); // 输出目录信息测试 // List<String> children = zkClient.getChildren("/"); // children.forEach(System.out::println); } @Override public void run() { // 初始化连接(在各个线程里开启连接,模拟分布式环境) connect(); String threadName = Thread.currentThread().getName(); // 竞争锁 while ( true ) { try { System.out.println(threadName + " 开始竞争锁..." ); // 创建zk临时节点 zkClient.createEphemeral( "/dl" , "test" ); System.out.println(threadName + " 获得锁!!!" ); // 获得锁后修改共享变量 CNT ++; System.out.println(threadName + " 释放了锁..." + CNT); zkClient.delete( "/dl" ); Thread.sleep( 2000 ); } catch (Exception e) { // 创建临时节点失败,表示未获得锁 System.out.println(threadName + " 未获得锁,将重试!!!" ); // System.out.println(e.getMessage()); try { Thread.sleep( 1500 ); } catch (InterruptedException e1) { e1.printStackTrace(); } } } } } |
3.测试结果
4.Zookeeper如何保证分布式环境下的线程安全(不重复创建节点)?
查看zookeeper源码发现,创建节点是同步方法
而写请求(创建节点)都是在master节点执行,而创建节点又做了同步控制,所以即使是分布式环境创建节点也是线程安全的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步