昨天我自己编写了一个基于ZooKeeper的分布式锁,但发现出现了死锁问题。今天我打算学习Curator中的InterProcessMutex,了解如何解决死锁。
Curator使用了可重入锁来解决死锁问题。
可重入锁指的是当一个服务已经获取了某个锁后,再次请求该锁时不会被阻塞,而是需要解锁的次数加一。
private boolean internalLock(long time, TimeUnit unit) throws Exception {
Thread currentThread = Thread.currentThread();
LockData lockData = (LockData)this.threadData.get(currentThread);
if (lockData != null) {
lockData.lockCount.incrementAndGet();
return true;
} else {
String lockPath =
this.internals.attemptLock(time,unit,this.getLockNodeBytes());
if (lockPath != null) {
LockData newLockData = new LockData(currentThread, lockPath);
this.threadData.put(currentThread, newLockData);
return true;
} else {
return false;
}
}
}
在Curator的实现中,通过维护一个LockData
对象来记录锁的状态,其中包括一个lockCount
字段,用于记录需要解锁的次数。
LockData lockData = (LockData)this.threadData.get(currentThread);
在internalLock
方法中,首先检查当前线程是否已经持有锁,如果是,则增加lockCount
;如果不是,则尝试获取锁并创建新的LockData
对象来记录锁的信息。
public void release() throws Exception {
Thread currentThread = Thread.currentThread();
LockData lockData = (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 {
this.internals.releaseLock(lockData.lockPath);
} finally {
this.threadData.remove(currentThread);
}
}
}
}
}
在release
方法中,会根据lockCount
的值来判断是否需要真正释放锁。只有当lockCount
减为0时,才会执行真正的释放操作(删除节点)。如果lockCount
大于0,则只是减少lockCount
的值,而不进行实际的节点操作。
简而言之,只有当lockCount
为0时,才会执行真正的节点操作。