1.實現步驟
- 首先要創建一個鎖的根節點,比如/mylock。
- 想要獲取鎖的客戶端在鎖的根節點下面創建znode,作爲/mylock的子節點,節點的類型要選擇 CreateMode.PERSISTENT_SEQUENTIAL,節點的名字”lock-“,假設目前同時有3個客戶端想要獲得鎖,那麼/mylock下的目錄應該是這個樣子的。
lock-0000000001,lock-0000000002,lock-0000000003
“lock-“是前綴 , 0000000001,0000000002,0000000003 是zk服務端自動生成的自增數字。
- 當前客戶端通過getChildren(/mylock)獲取所有子節點列表並根據自增數字排序,然後判斷一下自己創建的節點的順序是不是在列表當中最小的,如果是 那麼獲取到鎖,如果不是,那麼獲取自己的前一個節點,並設置監聽這個節點的變化,當節點變化時重新執行步驟3 直到自己是編號最小的一個爲止。
舉例:假設當前客戶端創建的節點是0000000002,因爲它的編號不是最小的,所以獲取不到鎖,那麼它就找到它前面的一個節點0000000001 並對它設置監聽。
- 釋放鎖,當前獲得鎖的客戶端在操作完成後刪除自己創建的節點,這樣會觸發zk的事件給其它客戶端知道,這樣其它客戶端會重新執行(步驟3)。
舉例:加入客戶端0000000001獲取到鎖,然後客戶端0000000002加入進來獲取鎖,發現自己不是編號最小的,那麼它會監聽它前面節點的事件(0000000001的事件)然後執行步驟(3),當客戶端0000000001操作完成後刪除自己的節點,這時zk服務端會發送事件,這時客戶端0000000002會接收到該事件,然後重複步驟3直到獲取到鎖)
2.實現代碼
package com.zookeeper.base;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class ZookeeperClient {
private static final int SESSION_TIMEOUT = 5000;
private static final String HOSTS = "127.0.0.1:2181";
private String groupNode = "mylock";
private String subNode = "lock-";
private ZooKeeper zk;
private String thisPath;
private String waitPath;
private CountDownLatch cdl = new CountDownLatch(1);
public void connectZookeeper() throws Exception {
zk = new ZooKeeper(HOSTS, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
try{
if(event.getState() == KeeperState.SyncConnected) {
if(EventType.None == event.getType()) {
System.out.println("建立連接...");
cdl.countDown();
}
else if(EventType.NodeDeleted == event.getType() &&
event.getPath().equals(waitPath)) {
List<String> childrenNodes = zk.getChildren("/"+groupNode, false);
String thisNode = thisPath.substring(("/"+groupNode+"/").length());
Collections.sort(childrenNodes);
int index = childrenNodes.indexOf(thisNode);
if(index == 0) {
doSomething();
} else {
waitPath = "/" + groupNode + "/" +childrenNodes.get(index-1);
if(zk.exists(waitPath, true) == null) {
doSomething();
}
}
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
});
cdl.await();
if(zk.exists("/"+groupNode, false) == null) {
zk.create("/"+groupNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
thisPath = zk.create("/"+groupNode+"/"+subNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
Thread.sleep(100);
List<String> childrenNodes = zk.getChildren("/"+groupNode, false);
if(childrenNodes.size() == 1) {
doSomething();
} else {
String thisNode = this.thisPath.substring(("/"+groupNode+"/").length());
Collections.sort(childrenNodes);
int index = childrenNodes.indexOf(thisNode);
if(index == -1) {
} else if (index == 0) {
doSomething();
} else {
this.waitPath = "/" + groupNode + "/" + childrenNodes.get(index-1);
zk.getData(waitPath, true, new Stat());
}
}
}
private void doSomething() throws Exception {
try {
System.out.println("gain lock:"+this.thisPath);
Thread.sleep(2000);
}catch(Exception e) {
e.printStackTrace();
}finally {
System.out.println("finshed:"+thisPath);
zk.delete(this.thisPath, -1);
}
}
public static void main(String[] args) throws Exception {
for(int i=0;i<10;i++) {
new Thread(){
@Override
public void run() {
try{
ZookeeperClient zc = new ZookeeperClient();
zc.connectZookeeper();
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
Thread.sleep(Integer.MAX_VALUE);
}
}