Java tutorial
/* * Copyright (C) 2010-2101 Alibaba Group Holding Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.locks.ReentrantLock; import org.I0Itec.zkclient.IZkConnection; import org.I0Itec.zkclient.exception.ZkException; import org.I0Itec.zkclient.exception.ZkInterruptedException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.NestableRuntimeException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.otter.shared.arbitrate.impl.zookeeper.AsyncWatcher; import com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient; import com.alibaba.otter.shared.common.utils.lock.BooleanMutex; import com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx; import com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx; /** * zookeeper?? <br/> * document : <br/> * <a href="http://zookeeper.apache.org/doc/trunk/recipes.html">http://zookeeper.apache.org/doc/trunk/recipes.html</a> * * <pre> * ? * {@linkplain ReentrantLock}ReentrantLock??????ReentrantLock * {@linkplain DistributedLock}??????DistributedLock * <strong>?DistributedLocklock/unlock?!!</strong> * * ? * <code> * DistributedLock lock = new DistributedLock("/lock/"); * try { * lock.lock(); * // do something * } catch (InterruptedException e1) { * // ? * } catch (KeeperException e1) { * // zookeeper * } finally { * try { * lock.unlock(); * } catch (KeeperException e) { * // zookeeper * } * } * </code> * </pre> * * @author jianghang 2011-9-29 ?11:16:07 * @version 4.0.0 */ public class DistributedLock { private static final Logger logger = LoggerFactory.getLogger(DistributedLock.class); private static final byte[] data = { 0x12, 0x34 }; // private static final Long DEFAULT_TIMEOUT_PERIOD = 60 * 1000L; private ZkClientx zookeeper = ZooKeeperClient.getInstance(); private final String root; // private String id; private LockNode idName; private String ownerId; private String lastChildId; private Throwable other = null; private KeeperException exception = null; private InterruptedException interrupt = null; public DistributedLock(String root) { this.root = root; ensureExists(root); } /** * ?????? */ public void lock() throws InterruptedException, KeeperException { // ?? if (exception != null) { throw exception; } if (interrupt != null) { throw interrupt; } if (other != null) { throw new NestableRuntimeException(other); } if (isOwner()) {// ?? return; } BooleanMutex mutex = new BooleanMutex(); acquireLock(mutex); mutex.get(); // ??zookeeper???watcher?? // try { // mutex.get(DEFAULT_TIMEOUT_PERIOD, TimeUnit.MILLISECONDS);// true // } catch (TimeoutException e) { // if (!mutex.state()) { // lock(); // } // } if (exception != null) { unlock(); throw exception; } if (interrupt != null) { unlock(); throw interrupt; } if (other != null) { unlock(); throw new NestableRuntimeException(other); } } /** * ???, ? * * @throws InterruptedException * @throws KeeperException */ public boolean tryLock() throws KeeperException { // ?? if (exception != null) { throw exception; } if (isOwner()) {// ?? return true; } acquireLock(null); if (exception != null) { unlock(); throw exception; } if (interrupt != null) { unlock(); Thread.currentThread().interrupt(); } if (other != null) { unlock(); throw new NestableRuntimeException(other); } return isOwner(); } /** * ? */ public void unlock() throws KeeperException { if (id != null) { zookeeper.delete(root + "/" + id); id = null; idName = null; } else { // do nothing } } private void ensureExists(final String path) { try { if (zookeeper.exists(path)) { return; } zookeeper.create(path, data, CreateMode.PERSISTENT); } catch (ZkInterruptedException e) { Thread.currentThread().interrupt(); interrupt = (InterruptedException) e.getCause(); } catch (ZkException e) { exception = (KeeperException) e.getCause(); } } /** * ?path */ public String getRoot() { return root; } /** * ???owner */ public boolean isOwner() { return id != null && ownerId != null && id.equals(ownerId); } /** * ?id */ public String getId() { return this.id; } // ===================== helper method ============================= /** * lock??watch????lock? */ private Boolean acquireLock(final BooleanMutex mutex) { try { do { if (id == null) {// ?lock long sessionId = getSessionId(); String prefix = "x-" + sessionId + "-"; // String path = zookeeper.create(root + "/" + prefix, data, CreateMode.EPHEMERAL_SEQUENTIAL); int index = path.lastIndexOf("/"); id = StringUtils.substring(path, index + 1); idName = new LockNode(id); } if (id != null) { List<String> names = zookeeper.getChildren(root); if (names.isEmpty()) { logger.warn("lock lost with scene:empty list, id[] and node[]", id, idName); unlock();// ?? } else { // ? SortedSet<LockNode> sortedNames = new TreeSet<LockNode>(); for (String name : names) { sortedNames.add(new LockNode(name)); } if (sortedNames.contains(idName) == false) { logger.warn("lock lost with scene:not contains ,id[] and node[]", id, idName); unlock();// ?? continue; } // ?ownerId ownerId = sortedNames.first().getName(); if (mutex != null && isOwner()) { mutex.set(true);// ? return true; } else if (mutex == null) { return isOwner(); } SortedSet<LockNode> lessThanMe = sortedNames.headSet(idName); if (!lessThanMe.isEmpty()) { // ? LockNode lastChildName = lessThanMe.last(); lastChildId = lastChildName.getName(); // watcher? IZkConnection connection = zookeeper.getConnection(); // zkclient?zk?lock??watcher?zk? ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper(); Stat stat = orginZk.exists(root + "/" + lastChildId, new AsyncWatcher() { public void asyncProcess(WatchedEvent event) { if (!mutex.state()) { // ?????lock acquireLock(mutex); } else { logger.warn("locked successful."); } } }); if (stat == null) { acquireLock(mutex);// ????watcher? } } else { if (isOwner()) { mutex.set(true); } else { logger.warn("lock lost with scene:no less ,id[] and node[]", id, idName); unlock();// ?idownerId?? } } } } } while (id == null); } catch (KeeperException e) { exception = e; if (mutex != null) { mutex.set(true); } } catch (InterruptedException e) { interrupt = e; if (mutex != null) { mutex.set(true); } } catch (Throwable e) { other = e; if (mutex != null) { mutex.set(true); } } if (isOwner() && mutex != null) { mutex.set(true); } return Boolean.FALSE; } private long getSessionId() { IZkConnection connection = zookeeper.getConnection(); return ((ZooKeeperx) connection).getZookeeper().getSessionId(); } }