可以继承 aqs 重写 tryAcquire 和 tryRelease 方法,在zookeeper里成功写入临时顺序节点就调用 compareAndSetState 方法修改state,业务处理结束删除临时顺序节点。不得不说 aqs 真的很方便。
所以换成信号量 CountDownLatch 来实现一个。
为了提高性能,应该避免羊群效应,改用监听前一个节点的删除事件。
public class ZkLock extends Lock {
private static final String LOCK_PATH = "/lock";
//当前节点路径
private String currentPath;
//前一个节点的路径
private String beforePath;
//zookeeper客户端
private ZKClient client;
private CountDownLatch countDownLatch = null;
public ZkLock(ZKClient client) {
this.client = client;
//如果不存在这个节点,则创建持久节点
if (!zkClient.exists(PATH)) {
zkClient.createPersistent(PATH);
}
}
@Override
public void lock() {
if (tryLock()) {
System.out.println("加锁成功");
} else {
System.out.println("等待锁释放");
waitLock();
}
}
@Override
public void releaseLock() {
if (null != client) {
client.delete(currentPath);
client.close();
}
}
@Override
public boolean tryLock() {
//如果currentPath为空则为第一次尝试加锁,第一次加锁赋值currentPath
if (null == currentPath || "".equals(currentPath)) {
//在path下创建一个临时的顺序节点
currentPath = client.createEphemeralSequential(PATH+"/", "lock");
}
//获取所有的临时节点,并排序
List<String> childrens = client.getChildren(PATH);
Collections.sort(childrens);
if (currentPath.equals(PATH+"/"+childrens.get(0))) {
return true;
}else {//如果当前节点不是排名第一,则获取它前面的节点名称,并赋值给beforePath
int pathLength = PATH.length();
int wz = Collections.binarySearch(childrens, currentPath.substring(pathLength+1));
beforePath = PATH+"/"+childrens.get(wz-1);
}
return false;
}
@Override
public void waitLock() {
IZkDataListener listener = new IZkDataListener() {
@Override
public void handleDataDeleted(String dataPath) throws Exception {
if (null != countDownLatch){
countDownLatch.countDown();
}
}
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
}
};
//监听前一个节点的变化
zkClient.subscribeDataChanges(beforePath, listener);
if (client.exists(beforePath)) {
countDownLatch = new CountDownLatch(1);
try {
// 阻塞等待前一个节点被删除,即没有锁
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
zkClient.unsubscribeDataChanges(beforePath, lIZkDataListener);
}
}