基本思路
1 client调用create()方法创建“/locks/lock”临时顺序节点,注意节点类型是EPHEMERAL_SEQUENTIAL
2 client调用getChildren("/locks",false)来获取所有已经创建的子节点,这里并不注册任何Watcher,只是为了看自己的是不是最小的节点
如果是,便获得了锁。
3 客户端获取到所有子节点Path后,如果发现自己在步骤1中创建的节点是所有节点中最小的,那么就认为这个客户端获得了锁 执行
4 如果在步骤3中,发现不是最小的,那么找到比自己小的那个节点,然后对其调用exist()方法注册事件监听,通过计数器阻塞
5 之后一旦这个被关注的节点移除,客户端会收到相应的通知,计数器countdown 不在阻塞 继续执行
代码如下:
public class MydistributedLock implements Watcher, Lock {
private ZooKeeper zk = null;
private String rootLockName = "/locks";
// zk connect counter
private CountDownLatch zkConnectCountDouwnLatch = new CountDownLatch(1);
// zk wait lock counter
private CountDownLatch waitCountDownLatch;
// current node name
private String curName;
// the node which current node is listening
private String waitName;
// competing resource
private String lockName;
private int zkTimeOut = 30000;
public MydistributedLock(String connectUrl, String lockName) throws Exception {
try {
zk = new ZooKeeper(connectUrl, zkTimeOut, this);
this.lockName = lockName;
zkConnectCountDouwnLatch.await();
Stat stat = zk.exists(rootLockName, false);
if (stat == null) {
zk.create(rootLockName, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void process(WatchedEvent event) {
org.apache.zookeeper.Watcher.Event.EventType eventType = event.getType();
if (eventType == org.apache.zookeeper.Watcher.Event.EventType.NodeDeleted) {
// if node is not exist,don't wait it
if (this.waitCountDownLatch != null) {
this.waitCountDownLatch.countDown();
}
// connection is success
else if (eventType == org.apache.zookeeper.Watcher.Event.EventType.None)
zkConnectCountDouwnLatch.countDown();
}
}
public void lock() {
if (tryLock()) {
return;
} else {
waitForLock();
}
}
private void waitForLock() {
try {
Stat stat = zk.exists(rootLockName + "/" + waitName, true);
if (stat != null) {
this.waitCountDownLatch = new CountDownLatch(1);
this.waitCountDownLatch.await();
this.waitCountDownLatch = null;
System.out.println(Thread.currentThread().getName() + "等到了lock");
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void lockInterruptibly() throws InterruptedException {
// TODO Auto-generated method stub
}
public boolean tryLock() {
try {
curName = zk.create(rootLockName + "/" + lockName + "_lock_", new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL);
List<String> subNodes = zk.getChildren(rootLockName, false);
Collections.sort(subNodes);
// get all the children nodes compared with current node to judge it if is the
// smallest
if (curName.equals(rootLockName + "/" + subNodes.get(0))) {
return true;
}
int curIndex = subNodes.indexOf(curName.split("/")[2]);
// if the current node is not the smallest,find the one in front
waitName = subNodes.get(curIndex - 1);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// TODO Auto-generated method stub
return false;
}
public void unlock() {
System.out.println(Thread.currentThread().getName() + "unlock");
try {
zk.delete(curName, -1);
curName = null;
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
}
}
测试代码:
public class TestLock {
static int n = 0;
public static void add() {
System.out.println(++n);
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
public void run() {
MydistributedLock lock = null;
try {
lock = new MydistributedLock("192.168.1.200:2081", "test");
lock.lock();
add();
System.out.println(Thread.currentThread().getName() + "is running");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (lock != null) {
lock.unlock();
}
}
}
};
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executor.submit(runnable);
}
executor.shutdown();
}
}
运行结果如下: