Zookeeper分布式锁实现

https://blog.csdn.net/evankaka/article/details/70570941


  摘要:本文要使用Zookeeper来实现一个分布式锁,是一个悲观锁。

    本文源码请在这里下载:https://github.com/appleappleapple/DistributeLearning

一、锁设计

  获取锁实现思路:
1. 首先创建一个作为锁目录(znode),通常用它来描述锁定的实体,称为:/lock_node
2. 希望获得锁的客户端在锁目录下创建znode,作为锁/lock_node的子节点,并且节点类型为有序临时节点(EPHEMERAL_SEQUENTIAL);
例如:有两个客户端创建znode,分别为/lock_node/lock-1和/lock_node/lock-2
3. 当前客户端调用getChildren(/lock_node)得到锁目录所有子节点,不设置watch,接着获取小于自己(步骤2创建)的兄弟节点
4. 步骤3中获取小于自己的节点不存在 && 最小节点与步骤2中创建的相同,说明当前客户端顺序号最小,获得锁,结束。
5. 客户端监视(watch)相对自己次小的有序临时节点状态
6. 如果监视的次小节点状态发生变化,则跳转到步骤3,继续后续操作,直到退出锁竞争。     

分布锁笔者这里就不做介绍了,来看看整个代码设计的流程图如下


二、代码

接下来我们就开始编程了~

1、DistributedLock接口定义

[java]  view plain  copy
  1. package com.github.distribute.lock;  
  2.   
  3. import java.util.concurrent.TimeUnit;  
  4.   
  5. public interface DistributedLock {  
  6.   
  7.     /** 
  8.      * 尝试获取锁,不进行等待。得到返回true, 
  9.      *  
  10.      * @return 
  11.      * @throws Exception 
  12.      */  
  13.     public boolean tryLock() throws Exception;  
  14.   
  15.     /** 
  16.      * 阻塞等待获取锁 
  17.      *  
  18.      * @throws Exception 
  19.      */  
  20.     public void lock() throws Exception;  
  21.   
  22.     /** 
  23.      * 在规定时间内等待获取锁 
  24.      *  
  25.      * @param time 
  26.      * @param unit 
  27.      * @return 
  28.      * @throws Exception 
  29.      */  
  30.     public boolean lock(long time, TimeUnit unit) throws Exception;  
  31.   
  32.     /** 
  33.      * 释放锁 
  34.      *  
  35.      * @throws Exception 
  36.      */  
  37.     public void unLock() throws Exception;  
  38.   
  39. }  

2、部分实现BaseDistributedLock

[java]  view plain  copy
  1. package com.github.distribute.zookeeper;  
  2.   
  3. import java.util.Collections;  
  4. import java.util.Comparator;  
  5. import java.util.List;  
  6. import java.util.concurrent.CountDownLatch;  
  7. import java.util.concurrent.TimeUnit;  
  8.   
  9. import org.apache.zookeeper.CreateMode;  
  10. import org.apache.zookeeper.WatchedEvent;  
  11. import org.apache.zookeeper.Watcher;  
  12. import org.apache.zookeeper.Watcher.Event.EventType;  
  13. import org.apache.zookeeper.ZooDefs;  
  14. import org.apache.zookeeper.ZooKeeper;  
  15. import org.apache.zookeeper.data.Stat;  
  16. import org.slf4j.Logger;  
  17. import org.slf4j.LoggerFactory;  
  18.   
  19. import com.github.distribute.lock.DistributedLock;  
  20.   
  21. public abstract class BaseDistributedLock implements DistributedLock {  
  22.   
  23.     private static Logger logger = LoggerFactory.getLogger(BaseDistributedLock.class);  
  24.   
  25.     private ZooKeeper zooKeeper;  
  26.     private String rootPath;// 根路径名  
  27.     private String lockNamePre;// 锁前缀  
  28.     private String currentLockPath;// 用于保存某个客户端在locker下面创建成功的顺序节点,用于后续相关操作使用(如判断)  
  29.     private static int MAX_RETRY_COUNT = 10;// 最大重试次数  
  30.       
  31.   
  32.       
  33.     /** 
  34.      * 初始化根目录 
  35.      */  
  36.     private void init() {  
  37.         try {  
  38.             Stat stat = zooKeeper.exists(rootPath, false);// 判断一下根目录是否存在  
  39.             if (stat == null) {  
  40.                 zooKeeper.create(rootPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
  41.             }  
  42.         } catch (Exception e) {  
  43.             logger.error("create rootPath error", e);  
  44.         }  
  45.     }  
  46.   
  47.     /** 
  48.      * 取得锁的排序号 
  49.      *  
  50.      * @param str 
  51.      * @param lockName 
  52.      * @return 
  53.      */  
  54.     private String getLockNodeNumber(String str, String lockName) {  
  55.         int index = str.lastIndexOf(lockName);  
  56.         if (index >= 0) {  
  57.             index += lockName.length();  
  58.             return index <= str.length() ? str.substring(index) : "";  
  59.         }  
  60.         return str;  
  61.     }  
  62.   
  63.     /** 
  64.      * 取得锁的排序列表 
  65.      *  
  66.      * @return 
  67.      * @throws Exception 
  68.      */  
  69.     private List<String> getSortedChildren() throws Exception {  
  70.         List<String> children = zooKeeper.getChildren(rootPath, false);  
  71.         if (children != null && !children.isEmpty()) {  
  72.             Collections.sort(children, new Comparator<String>() {  
  73.                 public int compare(String lhs, String rhs) {  
  74.                     return getLockNodeNumber(lhs, lockNamePre).compareTo(getLockNodeNumber(rhs, lockNamePre));  
  75.                 }  
  76.             });  
  77.         }  
  78.         logger.info("sort childRen:{}", children);  
  79.         return children;  
  80.     }  
  81.   
  82.     /** 
  83.      * 删除锁节点 
  84.      */  
  85.     private void deleteLockNode() {  
  86.         try {  
  87.             zooKeeper.delete(currentLockPath, -1);  
  88.         } catch (Exception e) {  
  89.             logger.error("unLock error", e);  
  90.   
  91.         }  
  92.     }  
  93.   
  94.     /** 
  95.      * 该方法用于判断自己是否获取到了锁,即自己创建的顺序节点在locker的所有子节点中是否最小.如果没有获取到锁,则等待其它客户端锁的释放, 
  96.      * 并且稍后重试直到获取到锁或者超时 
  97.      *  
  98.      * @param startMillis 
  99.      * @param millisToWait 
  100.      * @param ourPath 
  101.      * @return 
  102.      * @throws Exception 
  103.      */  
  104.     private boolean waitToLock(long startMillis, Long millisToWait) throws Exception {  
  105.   
  106.         boolean haveTheLock = false;  
  107.         boolean doDelete = false;  
  108.   
  109.         try {  
  110.             while (!haveTheLock) {  
  111.                 logger.info("get Lock Begin");  
  112.                 // 该方法实现获取locker节点下的所有顺序节点,并且从小到大排序,  
  113.                 List<String> children = getSortedChildren();  
  114.                 String sequenceNodeName = currentLockPath.substring(rootPath.length() + 1);  
  115.   
  116.                 // 计算刚才客户端创建的顺序节点在locker的所有子节点中排序位置,如果是排序为0,则表示获取到了锁  
  117.                 int ourIndex = children.indexOf(sequenceNodeName);  
  118.   
  119.                 /* 
  120.                  * 如果在getSortedChildren中没有找到之前创建的[临时]顺序节点,这表示可能由于网络闪断而导致 
  121.                  * Zookeeper认为连接断开而删除了我们创建的节点,此时需要抛出异常,让上一级去处理 
  122.                  * 上一级的做法是捕获该异常,并且执行重试指定的次数 见后面的 attemptLock方法 
  123.                  */  
  124.                 if (ourIndex < 0) {  
  125.                     logger.error("not find node:{}", sequenceNodeName);  
  126.                     throw new Exception("节点没有找到: " + sequenceNodeName);  
  127.                 }  
  128.   
  129.                 // 如果当前客户端创建的节点在locker子节点列表中位置大于0,表示其它客户端已经获取了锁  
  130.                 // 此时当前客户端需要等待其它客户端释放锁,  
  131.                 boolean isGetTheLock = ourIndex == 0;  
  132.   
  133.                 // 如何判断其它客户端是否已经释放了锁?从子节点列表中获取到比自己次小的哪个节点,并对其建立监听  
  134.                 String pathToWatch = isGetTheLock ? null : children.get(ourIndex - 1);  
  135.   
  136.                 if (isGetTheLock) {  
  137.                     logger.info("get the lock,currentLockPath:{}", currentLockPath);  
  138.                     haveTheLock = true;  
  139.                 } else {  
  140.                     // 如果次小的节点被删除了,则表示当前客户端的节点应该是最小的了,所以使用CountDownLatch来实现等待  
  141.                     String previousSequencePath = rootPath.concat("/").concat(pathToWatch);  
  142.                     final CountDownLatch latch = new CountDownLatch(1);  
  143.                     final Watcher previousListener = new Watcher() {  
  144.                         public void process(WatchedEvent event) {  
  145.                             if (event.getType() == EventType.NodeDeleted) {  
  146.                                 latch.countDown();  
  147.                             }  
  148.                         }  
  149.                     };  
  150.   
  151.                     // 如果节点不存在会出现异常  
  152.                     zooKeeper.exists(previousSequencePath, previousListener);  
  153.   
  154.                     // 如果有超时时间,刚到超时时间就返回  
  155.                     if (millisToWait != null) {  
  156.                         millisToWait -= (System.currentTimeMillis() - startMillis);  
  157.                         startMillis = System.currentTimeMillis();  
  158.                         if (millisToWait <= 0) {  
  159.                             doDelete = true// timed out - delete our node  
  160.                             break;  
  161.                         }  
  162.   
  163.                         latch.await(millisToWait, TimeUnit.MICROSECONDS);  
  164.                     } else {  
  165.                         latch.await();  
  166.                     }  
  167.                 }  
  168.             }  
  169.         } catch (Exception e) {  
  170.             // 发生异常需要删除节点  
  171.             logger.error("waitToLock exception", e);  
  172.             doDelete = true;  
  173.             throw e;  
  174.         } finally {  
  175.             // 如果需要删除节点  
  176.             if (doDelete) {  
  177.                 deleteLockNode();  
  178.             }  
  179.         }  
  180.         logger.info("get Lock end,haveTheLock=" + haveTheLock);  
  181.         return haveTheLock;  
  182.     }  
  183.   
  184.     /** 
  185.      * createLockNode用于在locker(basePath持久节点)下创建客户端要获取锁的[临时]顺序节点 
  186.      *  
  187.      * @param path 
  188.      * @return 
  189.      * @throws Exception 
  190.      */  
  191.     private String createLockNode(String path) throws Exception {  
  192.         Stat stat = zooKeeper.exists(rootPath, false);  
  193.         // 判断一下根目录是否存在  
  194.         if (stat == null) {  
  195.             zooKeeper.create(rootPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
  196.         }  
  197.         return zooKeeper.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);  
  198.     }  
  199.   
  200.     /** 
  201.      * 尝试获取锁,如果不加超时时间,阻塞等待。否则,就是加了超时的阻塞等待 
  202.      *  
  203.      * @param time 
  204.      * @param unit 
  205.      * @return 
  206.      * @throws Exception 
  207.      */  
  208.     protected Boolean attemptLock(long time, TimeUnit unit) throws Exception {  
  209.         final long startMillis = System.currentTimeMillis();  
  210.         final Long millisToWait = (unit != null) ? unit.toMillis(time) : null;  
  211.   
  212.         boolean hasTheLock = false;  
  213.         boolean isDone = false;  
  214.         int retryCount = 0;  
  215.   
  216.         // 网络闪断需要重试一试,最大重试次数MAX_RETRY_COUNT  
  217.         while (!isDone) {  
  218.             isDone = true;  
  219.             try {  
  220.                 currentLockPath = createLockNode(rootPath.concat("/").concat(lockNamePre));  
  221.                 hasTheLock = waitToLock(startMillis, millisToWait);  
  222.   
  223.             } catch (Exception e) {  
  224.                 if (retryCount++ < MAX_RETRY_COUNT) {  
  225.                     isDone = false;  
  226.                 } else {  
  227.                     throw e;  
  228.                 }  
  229.             }  
  230.         }  
  231.   
  232.         return hasTheLock;  
  233.     }  
  234. }  
waitToLock是最主要的代码

3、完整实现

[java]  view plain  copy
  1. package com.github.distribute.zookeeper;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Collections;  
  5. import java.util.Comparator;  
  6. import java.util.List;  
  7. import java.util.concurrent.CountDownLatch;  
  8. import java.util.concurrent.TimeUnit;  
  9.   
  10. import org.apache.zookeeper.CreateMode;  
  11. import org.apache.zookeeper.WatchedEvent;  
  12. import org.apache.zookeeper.Watcher;  
  13. import org.apache.zookeeper.Watcher.Event.EventType;  
  14. import org.apache.zookeeper.ZooDefs;  
  15. import org.apache.zookeeper.ZooKeeper;  
  16. import org.apache.zookeeper.data.Stat;  
  17. import org.slf4j.Logger;  
  18. import org.slf4j.LoggerFactory;  
  19.   
  20. import com.github.distribute.lock.DistributedLock;  
  21.   
  22. public class ZookeeperDistributeLock implements DistributedLock {  
  23.   
  24.     private static Logger logger = LoggerFactory.getLogger(ZookeeperDistributeLock.class);  
  25.   
  26.     public static void main(String[] args) throws IOException {  
  27.         ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181"60000null);  
  28.         ZookeeperDistributeLock myLock = new ZookeeperDistributeLock(zooKeeper, "/test""lock-");  
  29.         while (true) {  
  30.             try {  
  31.                 myLock.lock();  
  32.                 Thread.sleep(5000);  
  33.   
  34.             } catch (Exception e) {  
  35.   
  36.             } finally {  
  37.                 myLock.unLock();  
  38.             }  
  39.         }  
  40.   
  41.     }  
  42.   
  43.     private ZooKeeper zooKeeper;  
  44.     private String rootPath;// 根路径名  
  45.     private String lockNamePre;// 锁前缀  
  46.     private String currentLockPath;// 用于保存某个客户端在locker下面创建成功的顺序节点,用于后续相关操作使用(如判断)  
  47.     private static int MAX_RETRY_COUNT = 10;// 最大重试次数  
  48.   
  49.     public ZookeeperDistributeLock(ZooKeeper zookeeper, String rootPath, String lockNamePre) {  
  50.         logger.info("rootPath:{},lockNamePre:{}", rootPath, lockNamePre);  
  51.         this.zooKeeper = zookeeper;  
  52.         this.rootPath = rootPath;  
  53.         this.lockNamePre = lockNamePre;  
  54.         init();  
  55.     }  
  56.   
  57.     /** 
  58.      * 初始化根目录 
  59.      */  
  60.     private void init() {  
  61.         try {  
  62.             Stat stat = zooKeeper.exists(rootPath, false);// 判断一下根目录是否存在  
  63.             if (stat == null) {  
  64.                 zooKeeper.create(rootPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
  65.             }  
  66.         } catch (Exception e) {  
  67.             logger.error("create rootPath error", e);  
  68.         }  
  69.     }  
  70.   
  71.     /** 
  72.      * 取得锁的排序号 
  73.      *  
  74.      * @param str 
  75.      * @param lockName 
  76.      * @return 
  77.      */  
  78.     private String getLockNodeNumber(String str, String lockName) {  
  79.         int index = str.lastIndexOf(lockName);  
  80.         if (index >= 0) {  
  81.             index += lockName.length();  
  82.             return index <= str.length() ? str.substring(index) : "";  
  83.         }  
  84.         return str;  
  85.     }  
  86.   
  87.     /** 
  88.      * 取得锁的排序列表 
  89.      *  
  90.      * @return 
  91.      * @throws Exception 
  92.      */  
  93.     private List<String> getSortedChildren() throws Exception {  
  94.         List<String> children = zooKeeper.getChildren(rootPath, false);  
  95.         if (children != null && !children.isEmpty()) {  
  96.             Collections.sort(children, new Comparator<String>() {  
  97.                 public int compare(String lhs, String rhs) {  
  98.                     return getLockNodeNumber(lhs, lockNamePre).compareTo(getLockNodeNumber(rhs, lockNamePre));  
  99.                 }  
  100.             });  
  101.         }  
  102.         logger.info("sort childRen:{}", children);  
  103.         return children;  
  104.     }  
  105.   
  106.     /** 
  107.      * 该方法用于判断自己是否获取到了锁,即自己创建的顺序节点在locker的所有子节点中是否最小.如果没有获取到锁,则等待其它客户端锁的释放, 
  108.      * 并且稍后重试直到获取到锁或者超时 
  109.      *  
  110.      * @param startMillis 
  111.      * @param millisToWait 
  112.      * @param ourPath 
  113.      * @return 
  114.      * @throws Exception 
  115.      */  
  116.     private boolean waitToLock(long startMillis, Long millisToWait) throws Exception {  
  117.   
  118.         boolean haveTheLock = false;  
  119.         boolean doDelete = false;  
  120.   
  121.         try {  
  122.             while (!haveTheLock) {  
  123.                 logger.info("get Lock Begin");  
  124.                 // 该方法实现获取locker节点下的所有顺序节点,并且从小到大排序,  
  125.                 List<String> children = getSortedChildren();  
  126.                 String sequenceNodeName = currentLockPath.substring(rootPath.length() + 1);  
  127.   
  128.                 // 计算刚才客户端创建的顺序节点在locker的所有子节点中排序位置,如果是排序为0,则表示获取到了锁  
  129.                 int ourIndex = children.indexOf(sequenceNodeName);  
  130.   
  131.                 /* 
  132.                  * 如果在getSortedChildren中没有找到之前创建的[临时]顺序节点,这表示可能由于网络闪断而导致 
  133.                  * Zookeeper认为连接断开而删除了我们创建的节点,此时需要抛出异常,让上一级去处理 
  134.                  * 上一级的做法是捕获该异常,并且执行重试指定的次数 见后面的 attemptLock方法 
  135.                  */  
  136.                 if (ourIndex < 0) {  
  137.                     logger.error("not find node:{}", sequenceNodeName);  
  138.                     throw new Exception("节点没有找到: " + sequenceNodeName);  
  139.                 }  
  140.   
  141.                 // 如果当前客户端创建的节点在locker子节点列表中位置大于0,表示其它客户端已经获取了锁  
  142.                 // 此时当前客户端需要等待其它客户端释放锁,  
  143.                 boolean isGetTheLock = ourIndex == 0;  
  144.   
  145.                 // 如何判断其它客户端是否已经释放了锁?从子节点列表中获取到比自己次小的哪个节点,并对其建立监听  
  146.                 String pathToWatch = isGetTheLock ? null : children.get(ourIndex - 1);  
  147.   
  148.                 if (isGetTheLock) {  
  149.                     logger.info("get the lock,currentLockPath:{}", currentLockPath);  
  150.                     haveTheLock = true;  
  151.                 } else {  
  152.                     // 如果次小的节点被删除了,则表示当前客户端的节点应该是最小的了,所以使用CountDownLatch来实现等待  
  153.                     String previousSequencePath = rootPath.concat("/").concat(pathToWatch);  
  154.                     final CountDownLatch latch = new CountDownLatch(1);  
  155.                     final Watcher previousListener = new Watcher() {  
  156.                         public void process(WatchedEvent event) {  
  157.                             if (event.getType() == EventType.NodeDeleted) {  
  158.                                 latch.countDown();  
  159.                             }  
  160.                         }  
  161.                     };  
  162.   
  163.                     // 如果节点不存在会出现异常  
  164.                     zooKeeper.exists(previousSequencePath, previousListener);  
  165.   
  166.                     // 如果有超时时间,刚到超时时间就返回  
  167.                     if (millisToWait != null) {  
  168.                         millisToWait -= (System.currentTimeMillis() - startMillis);  
  169.                         startMillis = System.currentTimeMillis();  
  170.                         if (millisToWait <= 0) {  
  171.                             doDelete = true// timed out - delete our node  
  172.                             break;  
  173.                         }  
  174.   
  175.                         latch.await(millisToWait, TimeUnit.MICROSECONDS);  
  176.                     } else {  
  177.                         latch.await();  
  178.                     }  
  179.                 }  
  180.             }  
  181.         } catch (Exception e) {  
  182.             // 发生异常需要删除节点  
  183.             logger.error("waitToLock exception", e);  
  184.             doDelete = true;  
  185.             throw e;  
  186.         } finally {  
  187.             // 如果需要删除节点  
  188.             if (doDelete) {  
  189.                 unLock();  
  190.             }  
  191.         }  
  192.         logger.info("get Lock end,haveTheLock=" + haveTheLock);  
  193.         return haveTheLock;  
  194.     }  
  195.   
  196.     /** 
  197.      * createLockNode用于在locker(basePath持久节点)下创建客户端要获取锁的[临时]顺序节点 
  198.      *  
  199.      * @param path 
  200.      * @return 
  201.      * @throws Exception 
  202.      */  
  203.     private String createLockNode(String path) throws Exception {  
  204.         Stat stat = zooKeeper.exists(rootPath, false);  
  205.         // 判断一下根目录是否存在  
  206.         if (stat == null) {  
  207.             zooKeeper.create(rootPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
  208.         }  
  209.         return zooKeeper.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);  
  210.     }  
  211.   
  212.     /** 
  213.      * 尝试获取锁,如果不加超时时间,阻塞等待。否则,就是加了超时的阻塞等待 
  214.      *  
  215.      * @param time 
  216.      * @param unit 
  217.      * @return 
  218.      * @throws Exception 
  219.      */  
  220.     private Boolean attemptLock(long time, TimeUnit unit) throws Exception {  
  221.         final long startMillis = System.currentTimeMillis();  
  222.         final Long millisToWait = (unit != null) ? unit.toMillis(time) : null;  
  223.   
  224.         boolean hasTheLock = false;  
  225.         boolean isDone = false;  
  226.         int retryCount = 0;  
  227.   
  228.         // 网络闪断需要重试一试,最大重试次数MAX_RETRY_COUNT  
  229.         while (!isDone) {  
  230.             isDone = true;  
  231.             try {  
  232.                 currentLockPath = createLockNode(rootPath.concat("/").concat(lockNamePre));  
  233.                 hasTheLock = waitToLock(startMillis, millisToWait);  
  234.   
  235.             } catch (Exception e) {  
  236.                 if (retryCount++ < MAX_RETRY_COUNT) {  
  237.                     isDone = false;  
  238.                 } else {  
  239.                     throw e;  
  240.                 }  
  241.             }  
  242.         }  
  243.   
  244.         return hasTheLock;  
  245.     }  
  246.   
  247.     public boolean tryLock() throws Exception {  
  248.         logger.info("tryLock Lock Begin");  
  249.         // 该方法实现获取locker节点下的所有顺序节点,并且从小到大排序,  
  250.         List<String> children = getSortedChildren();  
  251.         String sequenceNodeName = currentLockPath.substring(rootPath.length() + 1);  
  252.   
  253.         // 计算刚才客户端创建的顺序节点在locker的所有子节点中排序位置,如果是排序为0,则表示获取到了锁  
  254.         int ourIndex = children.indexOf(sequenceNodeName);  
  255.   
  256.         if (ourIndex < 0) {  
  257.             logger.error("not find node:{}", sequenceNodeName);  
  258.             throw new Exception("节点没有找到: " + sequenceNodeName);  
  259.         }  
  260.   
  261.         // 如果当前客户端创建的节点在locker子节点列表中位置大于0,表示其它客户端已经获取了锁  
  262.         return ourIndex == 0;  
  263.     }  
  264.   
  265.     public void lock() throws Exception {  
  266.         // -1,null表示阻塞等待,不设置超时时间  
  267.         attemptLock(-1null);  
  268.   
  269.     }  
  270.   
  271.     public boolean lock(long time, TimeUnit unit) throws Exception {  
  272.         if (time <= 0) {  
  273.             throw new Exception("Lock wait for time must greater than 0");  
  274.         }  
  275.   
  276.         if (unit == null) {  
  277.             throw new Exception("TimeUnit can not be null");  
  278.         }  
  279.   
  280.         return attemptLock(time, unit);  
  281.     }  
  282.   
  283.     public void unLock() {  
  284.         try {  
  285.             zooKeeper.delete(currentLockPath, -1);  
  286.         } catch (Exception e) {  
  287.             logger.error("unLock error", e);  
  288.   
  289.         }  
  290.   
  291.     }  
  292.   
  293. }  


三、对比

在文章Redis分布式锁----悲观锁实现,以秒杀系统为例,我们用redis也实现了分布式锁。zk的方案最大的优势在于避免结点挂掉后导致的死锁;redis的方案最大的优势在于性能超强;在实际生产过程中,结合自身情况来决定最适合的分布式锁。