我不知道我是否理解您的问题,正如您所说,当您锁定子树进行写入时,整个结构都被锁定。因此,简单的解决方案是为整个结构设置一个 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;
}
如果无法获得任何锁,则解锁已锁定的内容并稍后重试(您可以使用全局条件变量、超时等来实现此目的)。
编辑:添加代码以读锁定/写锁定树