转载自:http://blog.csdn.net/java2000_wl/article/details/8694270
获取锁实现思路:
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,继续后续操作,直到退出锁竞争。
- public synchronized boolean lock() throws KeeperException, InterruptedException {
- if (isClosed()) {
- return false;
- }
-
- ensurePathExists(dir);
-
-
-
-
- return (Boolean) retryOperation(zop);
- }
创建锁目录
- protected void ensurePathExists(String path) {
- ensureExists(path, null, acl, CreateMode.PERSISTENT);
- }
- protected void ensureExists(final String path, final byte[] data,
- final List<ACL> acl, final CreateMode flags) {
- try {
- retryOperation(new ZooKeeperOperation() {
- public boolean execute() throws KeeperException, InterruptedException {
-
- Stat stat = zookeeper.exists(path, false);
-
- if (stat != null) {
- return true;
- }
-
-
-
- zookeeper.create(path, data, acl, flags);
- return true;
- }
- });
- } catch (KeeperException e) {
- LOG.warn("Caught: " + e, e);
- } catch (InterruptedException e) {
- LOG.warn("Caught: " + e, e);
- }
- }
创建锁节点,获得锁目录下的所有节点, 如果为最小节点 获得锁成功
-
-
-
-
-
- public boolean execute() throws KeeperException, InterruptedException {
- do {
- if (id == null) {
- long sessionId = zookeeper.getSessionId();
- String prefix = "x-" + sessionId + "-";
-
-
- findPrefixInChildren(prefix, zookeeper, dir);
- idName = new ZNodeName(id);
- }
- if (id != null) {
- List<String> names = zookeeper.getChildren(dir, false);
- if (names.isEmpty()) {
- LOG.warn("No children in: " + dir + " when we've just " +
- "created one! Lets recreate it...");
-
- id = null;
- } else {
-
- SortedSet<ZNodeName> sortedNames = new TreeSet<ZNodeName>();
- for (String name : names) {
- sortedNames.add(new ZNodeName(dir + "/" + name));
- }
-
- ownerId = sortedNames.first().getName();
-
- SortedSet<ZNodeName> lessThanMe = sortedNames.headSet(idName);
- if (!lessThanMe.isEmpty()) {
- ZNodeName lastChildName = lessThanMe.last();
- lastChildId = lastChildName.getName();
- if (LOG.isDebugEnabled()) {
- LOG.debug("watching less than me node: " + lastChildId);
- }
-
- Stat stat = zookeeper.exists(lastChildId, new LockWatcher());
- if (stat != null) {
- return Boolean.FALSE;
- } else {
- LOG.warn("Could not find the" +
- " stats for less than me: " + lastChildName.getName());
- }
- } else {
-
- if (isOwner()) {
- if (callback != null) {
- callback.lockAcquired();
- }
-
- return Boolean.TRUE;
- }
- }
- }
- }
- }
- while (id == null);
- return Boolean.FALSE;
- }
- };
- private void findPrefixInChildren(String prefix, ZooKeeper zookeeper, String dir)
- throws KeeperException, InterruptedException {
-
- List<String> names = zookeeper.getChildren(dir, false);
- for (String name : names) {
-
- if (name.startsWith(prefix)) {
- id = name;
- if (LOG.isDebugEnabled()) {
- LOG.debug("Found id created last time: " + id);
- }
- break;
- }
- }
-
- if (id == null) {
-
- id = zookeeper.create(dir + "/" + prefix, data,
- getAcl(), EPHEMERAL_SEQUENTIAL);
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Created id: " + id);
- }
- }
释放锁:
释放锁非常简单,删除步骤1中创建的有序临时节点。另外,如果客户端进程死亡或连接失效,对应的节点也会被删除。
- public synchronized void unlock() throws RuntimeException {
-
- if (!isClosed() && id != null) {
-
-
-
- try {
-
- ZooKeeperOperation zopdel = new ZooKeeperOperation() {
- public boolean execute() throws KeeperException,
- InterruptedException {
-
- zookeeper.delete(id, -1);
- return Boolean.TRUE;
- }
- };
- zopdel.execute();
- } catch (InterruptedException e) {
- LOG.warn("Caught: " + e, e);
-
- Thread.currentThread().interrupt();
- } catch (KeeperException.NoNodeException e) {
-
- } catch (KeeperException e) {
- LOG.warn("Caught: " + e, e);
- throw (RuntimeException) new RuntimeException(e.getMessage()).
- initCause(e);
- }
- finally {
- if (callback != null) {
- callback.lockReleased();
- }
- id = null;
- }
- }
- }