如果对Curator或zookeeper加锁不明白的可以参考我的这两篇文章:
Redis分布式锁与Zk分布式锁的详解与选型
zookeeper使用过程中需要注意的坑
1. 加锁过程
// 一直尝试加锁,简称无终止
public void acquire() throws Exception {
if (!this.internalLock(-1L, (TimeUnit)null)) {
throw new IOException("Lost connection while trying to acquire lock: " + this.basePath);
}
}
// 指定时间内加锁,超过指定时间如果加锁失败则放弃加锁
public boolean acquire(long time, TimeUnit unit) throws Exception {
return this.internalLock(time, unit);
}
private boolean internalLock(long time, TimeUnit unit) throws Exception {
// 获取当前线程名称;
Thread currentThread = Thread.currentThread();
// ConcurrentMap<Thread, InterProcessMutex.LockData> threadData
// 从map中获取当前线程对应的锁相关数据;
InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
//如果lockData不为空,说明当前线程已经对该key加过锁
if (lockData != null) {
// 加锁次数+1,表示锁重入功能;
lockData.lockCount.incrementAndGet();
// 返回成功;
return true;
} else {
// this.getLockNodeBytes()=null;
// 此方法尝试去加锁;
String lockPath = this.internals.attemptLock(time, unit, this.getLockNodeBytes());
if (lockPath != null) {
// 返回不为null,表示加锁成功,将当前线程和LockData存到threadData map中;
InterProcessMutex.LockData newLockData = new InterProcessMutex.LockData(currentThread, lockPath);
this.threadData.put(currentThread, newLockData);
return true;
} else {
return false;
}
}
}
String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception {
long startMillis = System.currentTimeMillis();
// 转换时间
Long millisToWait = unit != null ? unit.toMillis(time) : null;
byte[] localLockNodeBytes = this.revocable.get() != null ? new byte[0] : lockNodeBytes;
int retryCount = 0;
String ourPath = null;
boolean hasTheLock = false;
boolean isDone = false;
while(!isDone) {
isDone = true;
try {
// 这两行代码算是加锁的核心了;
// 创建加锁节点
ourPath = this.driver.createsTheLock(this.client, this.path, localLockNodeBytes);
// 循环加锁
hasTheLock = this.internalLockLoop(startMillis, millisToWait, ourPath);
} catch (KeeperException.NoNodeException var14) {
if (!this.client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper())) {
throw var14;
}
isDone = false;
}
}
return hasTheLock ? ourPath : null;
}
// 创建加锁节点
public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception {
String ourPath;
if (lockNodeBytes != null) {
ourPath = (String)((ACLBackgroundPathAndBytesable)client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)).forPath(path, lockNodeBytes);
} else {
// 判断父节点是否存在,比如我们加锁的路径:/test/lock/key
// 会先判断key是否创建,如果没有创建,则先创建key,然后在key的路径下面创建一个临时有序节点
ourPath = (String)((ACLBackgroundPathAndBytesable)client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)).forPath(path);
}
return ourPath;
}
private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception {
boolean haveTheLock = false;
boolean doDelete = false;
try {
if (this.revocable.get() != null) {
((BackgroundPathable)this.client.getData().usingWatcher(this.revocableWatcher)).forPath(ourPath);
}
// 每次循环时判断client的状态是否正常和加锁是否成功;
while(this.client.getState() == CuratorFrameworkState.STARTED && !haveTheLock) {
// 获取当前key下所有临时节点并升序排序;
List<String> children = this.getSortedChildren();
// 截取当前加锁的序号是多少
String sequenceNodeName = ourPath.substring(this.basePath.length() + 1);
// 判断是否当前节点是否是最小节点,如果不是则返回上线级
PredicateResults predicateResults = this.driver.getsTheLock(this.client, children, sequenceNodeName, this.maxLeases);
// 如果为true,说明是最小节点,表示加锁成功;
if (predicateResults.getsTheLock()) {
haveTheLock = true;
} else {
// 拼装比当前节点小1的全路径
String previousSequencePath = this.basePath + "/" + predicateResults.getPathToWatch();
// 至于这个synchronized在这有何意义,需要各位自行搜索:wait为什么必须要synchronized中使用;
synchronized(this) {
try {
// 监听上一个节点
// 这个watcher里面会有一个方法:
// 如果有节点删除事件,会触发notifyAll,通知下面的wait事件;
// private synchronized void notifyFromWatcher() {
// this.notifyAll();
// }
((BackgroundPathable)this.client.getData().usingWatcher(this.watcher)).forPath(previousSequencePath);
// 如果没有传入锁获取时长,则一直等待获取;
if (millisToWait == null) {
this.wait();
} else {
// 指定了waitTime时,则超过waitTime时则不再等待;
// 超时后,会返回false,因为haveTheLock的值没有设置为true;
millisToWait = millisToWait - (System.currentTimeMillis() - startMillis);
startMillis = System.currentTimeMillis();
if (millisToWait > 0L) {
this.wait(millisToWait);
} else {
// 设置删除节点标识,在下面的finally只删除节点;
doDelete = true;
break;
}
}
} catch (KeeperException.NoNodeException var19) {
}
}
}
}
} catch (Exception var21) {
ThreadUtils.checkInterrupted(var21);
doDelete = true;
throw var21;
} finally {
if (doDelete) {
this.deleteOurPath(ourPath);
}
}
return haveTheLock;
}
public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception {
// 获取当前节点在children的位置;
int ourIndex = children.indexOf(sequenceNodeName);
validateOurIndex(sequenceNodeName, ourIndex);
// 当前ourIndex下标是否<1;如果小于1,表示当前节点是最小节点;
boolean getsTheLock = ourIndex < maxLeases;
// 如果它不是最小节点,则返回比它小1的节点;
String pathToWatch = getsTheLock ? null : (String)children.get(ourIndex - maxLeases);
return new PredicateResults(pathToWatch, getsTheLock);
}
2. 释放锁过程
// 释放锁的代码相对于说就简单很多
public void release() throws Exception {
// 获取当前线程,去map中判断当前线程是否存在;
Thread currentThread = Thread.currentThread();
InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
if (lockData == null) {
throw new IllegalMonitorStateException("You do not own the lock: " + this.basePath);
} else {
// 判断锁重入
int newLockCount = lockData.lockCount.decrementAndGet();
if (newLockCount <= 0) {
if (newLockCount < 0) {
throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + this.basePath);
} else {
try {
// 删除节点,删除节点时,下一个等待加锁节点会收到watcher通知
this.internals.releaseLock(lockData.lockPath);
} finally {
this.threadData.remove(currentThread);
}
}
}
}
}