一、定义接口
public interface Lock {
public void getLock();
public void unLock();
}
二、流程模板
public abstract class ZookeeperAbstractLock implements Lock {
private static final String ZookeeperConnectString = "192.168.xx.xx:2181";
//创建zk连接
protected ZkClient zkClient = new ZkClient(ZookeeperConnectString);
protected static final String PATH = "/lock";
protected static final String PATH2 = "/lock2";
public void getLock() {
if (tryLock()) {
System.out.println("获取zk锁资源...");
} else {
//等待
waitLock();
//重新获取锁资源
getLock();
}
}
//获取锁资源
abstract boolean tryLock();
//等待
abstract void waitLock();
}
三、实现
- 子类继承抽象类,实现获取锁,等待锁和释放锁的方法即可。
基于zk异常的实现
public class ZookeeperDistributeLock extends ZookeeperAbstractLock {
private CountDownLatch countDownLatch = null;
@Override
boolean tryLock() {
try {
//创建临时节点,创建成功则返回
zkClient.createEphemeral(PATH);
return true;
} catch (Exception e) {
//e.printStackTrace();
//System.out.println("获取锁失败...");
return false;
}
}
@Override
void waitLock() {
IZkDataListener listener = new IZkDataListener() {
public void handleDataChange(String dataPath, Object data) throws Exception {
}
public void handleDataDeleted(String dataPath) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
};
//注册事件
zkClient.subscribeDataChanges(PATH, listener);
if (zkClient.exists(PATH)) {
countDownLatch = new CountDownLatch(1);
try {
//如果起那么的节点一直存在,自己就等着,直到前面的节点被删除,方法再返回,
// 这里的await就相当于一直阻塞在这里等着,删除的动作在listen里面实现
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
//删除监听
zkClient.unsubscribeDataChanges(PATH, listener);
}
public void unLock() {
if (zkClient != null) {
zkClient.delete(PATH);
zkClient.close();
System.out.println("释放锁资源...");
}
}
}
基于监听的实现
public class ZookeeperDistributeListenLock extends ZookeeperAbstractLock {
private String beforePath;
private String currentPath;
private CountDownLatch countDownLatch = null;
public ZookeeperDistributeListenLock() {
if (!zkClient.exists(PATH2)) {
zkClient.createPersistent(PATH2);
}
}
@Override
boolean tryLock() {
//如果currentPath为空,则尝试加锁,第一次加锁赋值currentPath,并创建临时有序节点
if (currentPath == null || currentPath.length() < 1) {
currentPath = zkClient.createEphemeralSequential(PATH2 + '/', "lock");
}
//获取所有临时节点并且排序
List<String> childrens = zkClient.getChildren(PATH2);
Collections.sort(childrens);
//如果自己是第一个节点,那么就认为自己获取到锁了
if (currentPath.equals(PATH2 + '/' + childrens.get(0))) {
return true;
} else {
//如果当前节点不是排名第一,那么就获取其前面一个节点,并在后面监听前面的节点
int wz = Collections.binarySearch(childrens, currentPath.substring(7));
beforePath = PATH2 + '/' + childrens.get(wz - 1);
}
return false;
}
@Override
void waitLock() {
IZkDataListener listener = new IZkDataListener() {
public void handleDataChange(String dataPath, Object data) throws Exception {
}
public void handleDataDeleted(String dataPath) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
};
//监听前面一个节点是否被删除,前面节点一旦被删除,自己就有机会获取锁了
zkClient.subscribeDataChanges(beforePath, listener);
if (zkClient.exists(beforePath)) {
countDownLatch = new CountDownLatch(1);
try {
//如果起那么的节点一直存在,自己就等着,直到前面的节点被删除,方法再返回,
// 这里的await就相当于一直阻塞在这里等着,删除的动作在listen里面实现
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
//删除监听
zkClient.unsubscribeDataChanges(beforePath, listener);
}
public void unLock() {
if (zkClient != null) {
zkClient.delete(currentPath);
zkClient.close();
System.out.println("释放锁资源...");
}
}
}
四、源码