ZooKeeper(七)-- ZK原生API实现分布式锁

一、使用场景

在分布式应用,往往存在多个进程提供同一服务。这些进程有可能在相同的机器上,也有可能分布在不同的机器上。 如果这些进程共享了一些资源,可能就需要分布式锁来锁定对这些资源的访问。

二、实现分布式锁结构图

三、代码实现

   
   
  1. package com.xbq.zookeeper.javaApi;

  2. import java.util.Collections;

  3. import java.util.List;

  4. import java.util.concurrent.CountDownLatch;

  5. import java.util.concurrent.ExecutorService;

  6. import java.util.concurrent.Executors;

  7. import java.util.concurrent.Semaphore;

  8. import java.util.concurrent.TimeUnit;

  9. import org.apache.zookeeper.CreateMode;

  10. import org.apache.zookeeper.KeeperException;

  11. import org.apache.zookeeper.WatchedEvent;

  12. import org.apache.zookeeper.Watcher;

  13. import org.apache.zookeeper.ZooDefs.Ids;

  14. import org.apache.zookeeper.ZooKeeper;

  15. import org.apache.zookeeper.data.Stat;

  16. /**

  17. * 使用Zookeeper原生API实现分布式锁

  18. * @author xbq

  19. */

  20. public class ZookeeperLock implements Watcher{

  21. // 声明zk对象

  22. private ZooKeeper zk = null;

  23. // 此demo使用的集群,所以有多个ip和端口

  24. private static String CONNECT_SERVER = "192.168.242.130:2181,192.168.242.130:2182,192.168.242.130:2183";

  25. // session过期时间

  26. private static int SESSION_TIMEOUT = 3000;

  27. // 根节点

  28. private String root = "/locks";

  29. // 当前等待的节点

  30. private String waitNode;

  31. // 等待的时间

  32. private int waitTime;

  33. // 锁节点

  34. private String myzkNode;

  35. // 计数器

  36. private CountDownLatch latch;

  37. /**

  38. * 构造函数 初始化

  39. */

  40. public ZookeeperLock(){

  41. try {

  42. zk = new ZooKeeper(CONNECT_SERVER, SESSION_TIMEOUT, this);

  43. // 判断是否存在根节点,不需要监听根节点

  44. Stat stat = zk.exists(root, false);

  45. // 为空,说明不存在

  46. if(stat == null){

  47. // 添加一个永久节点,即添加一个根节点

  48. zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

  49. }

  50. } catch (Exception e) {

  51. e.printStackTrace();

  52. }

  53. }

  54. /**

  55. * 尝试获取锁

  56. * @return

  57. */

  58. private boolean tryLock(){

  59. String splitStr = "lock_"; // 格式 lock_000000001

  60. try {

  61. // 创建一个临时有序节点,并赋值给 myzkNode

  62. myzkNode = zk.create(root + "/" + splitStr, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

  63. // 获取根节点下的所有子节点

  64. List<String> children = zk.getChildren(root, false);

  65. // 对子节点 排序

  66. Collections.sort(children);

  67. // 如果刚刚创建的节点 等于 获取最小的一个子节点,则说明 获取到锁

  68. if(myzkNode.equals(root + "/" + children.get(0))){

  69. return true;

  70. }

  71. // 如果刚刚创建的节点 不等于 获取到的最小的一个子节点,则 监控比自己小的一个节点

  72. // 获取刚刚建立的子节点(不包含根节点的子节点)

  73. String childNode = myzkNode.substring(myzkNode.lastIndexOf("/") + 1);

  74. // 获取比自己小的节点

  75. waitNode = children.get(Collections.binarySearch(children, childNode) - 1);

  76. } catch (KeeperException e) {

  77. e.printStackTrace();

  78. } catch (InterruptedException e) {

  79. e.printStackTrace();

  80. }

  81. return false;

  82. }

  83. /**

  84. * 等待锁释放

  85. * @param waitNode

  86. * @param waidTime

  87. * @return

  88. * @throws KeeperException

  89. * @throws InterruptedException

  90. */

  91. private boolean waitLock(String waitNode, int waidTime) throws KeeperException, InterruptedException{

  92. // 判断比自己小的节点是否存在,并添加监听

  93. Stat stat = zk.exists(root + "/" + waitNode, true);

  94. // 如果存在 比自己小的节点

  95. if(stat != null){

  96. this.latch = new CountDownLatch(1);

  97. this.latch.await(waidTime, TimeUnit.MILLISECONDS);

  98. this.latch = null;

  99. }

  100. return true;

  101. }

  102. /**

  103. * 获取锁

  104. */

  105. public void lock(){

  106. // 如果尝试获取锁成功

  107. if(tryLock()){

  108. // 获取 成功

  109. System.out.println("Thread" + Thread.currentThread().getName() + " -- hold lock!");

  110. return;

  111. }

  112. // 等待并获取锁

  113. try {

  114. waitLock(waitNode, waitTime);

  115. } catch (KeeperException e) {

  116. e.printStackTrace();

  117. } catch (InterruptedException e) {

  118. e.printStackTrace();

  119. }

  120. }

  121. /**

  122. * 解锁

  123. */

  124. public void unLock(){

  125. try {

  126. zk.delete(myzkNode, -1);

  127. zk.close();

  128. System.out.println("Thread" + Thread.currentThread().getName() +" unlock success! 锁节点:" + myzkNode);

  129. } catch (InterruptedException e) {

  130. e.printStackTrace();

  131. } catch (KeeperException e) {

  132. e.printStackTrace();

  133. }

  134. }

  135. /**

  136. * 删除的时候 触发事件

  137. */

  138. @Override

  139. public void process(WatchedEvent event) {

  140. // 如果计数器不为空的话,释放计数器锁

  141. if(this.latch != null){

  142. this.latch.countDown();

  143. }

  144. }

  145. /**

  146. * 测试

  147. * @param args

  148. */

  149. public static void main(String[] args) {

  150. // 定义线程池

  151. ExecutorService service = Executors.newCachedThreadPool();

  152. // 只能10个线程同时运行,即模拟并发数为10

  153. final Semaphore semaphore = new Semaphore(10);

  154. // 10个客户端连接

  155. for(int i=0;i<10;i++){

  156. Runnable runnable = new Runnable() {

  157. @Override

  158. public void run() {

  159. try {

  160. semaphore.acquire();

  161. ZookeeperLock zkLock = new ZookeeperLock();

  162. zkLock.lock();

  163. // 业务操作代码

  164. Thread.sleep(3000);

  165. zkLock.unLock();

  166. semaphore.release();

  167. } catch (Exception e) {

  168. e.printStackTrace();

  169. }

  170. }

  171. };

  172. service.execute(runnable);

  173. }

  174. service.shutdown();

  175. }

  176. }

四、源码下载

点击阅读原文下载源码哦

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值