我正在寻找一个高效的系统,该系统具有一系列分层组织的读/写锁,以管理对分层组织的资源的访问。如果一个子树被锁定以进行写入,那么在整个子树中不应该获得其他锁,直到它被释放;类似地,子树中的写锁定应该防止父级中的锁定。
以下是我思考的想法:
-
使用Apache Commons事务。不幸的是,该项目自2008年3月以来一直没有更新,并被非正式终止。一些API文档似乎表明,即将发布的版本(1.3或2.0)将包括某种层次锁定,但找不到来源,似乎我们无法再访问他们的SVN存储库。
-
使用一系列
ReentrantReadWriteLock
s,我将按层次结构对其进行组织。我不是并发专家,我有点害怕自己去做。初步的想法似乎表明,即使在我可以尝试锁定子树之前,我也必须在管理ReentrantReadWriteLock
本身的整个结构上使用外部锁——因此,即使释放锁,我也不得不使用外部锁… -
使用
java.util.concurrent
和java.util.concurrent.atomic
中的类以比使用一系列ReentrantReadWriteLock
更有效的方式实现分层锁。
我已经准备好走最后一条路了,但我很惊讶没有发现任何现有的图书馆能更好地解决这个问题。因此:
- 我是否错过了一些显而易见的解决方案
- 还是这个问题特别难以妥善解决
我不知道我是否完全理解你的问题,正如你所说,当你锁定子树进行写入时,整个结构都被锁定了。因此,简单的解决方案是为整个结构设置一个RW锁。
顺便说一句,java.util.concurrent.atomic
对你的帮助不会超过一棵RW锁。
如果您希望能够独立地锁定同级,可以使用第二种解决方案(一个锁树,其中每个节点都有对其父节点的引用)。
锁定一个节点将使用其写锁来锁定它,并使用读锁来锁定每个父节点。父级不能在子级锁定的情况下被锁定,因为您无法获取其写锁,因为锁定子级已经获取了读锁。只有当没有其他线程写锁定任何父线程时,才允许锁定子线程。
上述锁是专用锁。(读写锁的另一个名称是共享独占锁)
要添加共享锁,每个节点还需要一个原子整数来指示:如果为正,则为间接写锁定子级的数量;如果为负数,则表示节点被读取锁定的次数。此外,节点及其父节点将被读取锁定,以避免在父节点上获取新的写入锁定。
伪代码:
Node {
// fields
parent: Node
lock: RWLock
count: AtomicInteger
}
public boolean trylocktree(node: Node, exclusive: boolean) {
if (exclusive) {
return trylocktree_ex(node, true);
} else {
return trylocktree_sh(node);
}
}
private boolean switch_count(i: AtomicInteger, diff: int) {
// adds diff to i if the sign of i is the same as the sign of diff
while (true) {
int v = i.get();
if (diff > 0 ? v < 0 : v > 0)
return false;
if (i.compareAndSet(v, v + diff))
return true;
}
}
private boolean trylocktree_ex(node: Node, writing: boolean) {
// check if a node is read-locked
if (!switch_count(node.count, 1))
return false;
// lock using the lock type passed as an arg
if (!node.lock(writing).trylock()) {
node.count--;
return false;
}
// read-lock every parent
if (!trylocktree_ex(node.parent, false)) {
node.count--
node.lock(writing).unlock();
return false;
}
return true;
}
private boolean trylocktree_sh(node: Node) {
// mark as shared-locked subtree
if (!switch_count(node.count, -1))
return false;
// get shared-lock on parents
if (!readlock_recursively(node)) {
node.count++;
return false;
}
return true;
}
private boolean readlock_recursively(node: Node) {
if (!node.lock(false).trylock())
return false;
if (!readlock_recursively(node.parent)) {
node.lock(false).unlock();
return false;
}
return true;
}
如果无法获取任何锁定,则解锁已锁定的内容,然后重试(可以使用全局条件变量、超时等来实现此目的)。
EDIT:添加了读取锁定/写入锁定树的代码
我将选择您自己的解决方案,并以Apache Commons Transaction algorithm给出的算法为起点。您可以使用ReentrantReadWriteLock,尽管通常这种锁更适合一个生产者多个读者的情况,这可能不是您想要的。看起来您的锁更像是一个常规的可重入互斥锁。