在Java中使用什么策略进行分层重入读/写锁定?

时间:2011-05-27 15:19:35

标签: java locking hierarchical reentrancy readwritelock

我正在寻找高效的系统,以便按层次组织一系列读/写锁来管理对分层组织资源的访问。如果一个子树被锁定以进行写入,那么在它被释放之前,不应该在整个子树中获得其他锁定;类似地,子树中的写锁定应该可以防止锁定父节点。

以下是我正在考虑的想法:

  • 使用Apache Commons Transaction 。不幸的是,该项目自2008年3月以来一直没有更新,并且已经非正式终止。一些API文档似乎表明即将推出的版本(1.3或2.0)将包含some kind of hierarchical locking,但是无法找到源代码,似乎我们无法再访问其SVN存储库。

  • 使用一系列ReentrantReadWriteLocks ,我会按层次结构组织。我不是并发专家,我有点害怕自己这样做。初步的想法似乎表明,即使在我尝试锁定子树之前,我必须在管理ReentrantReadWriteLock本身的整个结构上使用外部锁 - 这样即使释放锁< / em>我必须使用外锁...

  • 使用java.util.concurrentjava.util.concurrent.atomic 中的类以更有效的方式实现我的分层锁定,而不是使用一系列ReentrantReadWriteLock

我已准备好走最后一条路,但我很惊讶没有找到任何可以更好地解决这个问题的现有图书馆。所以:

  • 我错过了一些明显的解决方案吗?
  • 或者这个问题特别难以妥善解决?

2 个答案:

答案 0 :(得分:4)

我不知道我是否理解你的问题,正如你所说当你锁定一个子树进行写入时,整个结构都被锁定了。 因此,简单的解决方案是为整个结构设置一个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;
}

如果无法获取任何锁定,则解锁锁定的内容并稍后重试(您可以使用全局条件变量,超时等来实现此目的)。

编辑:添加了代码以读取锁定/写入锁定树

答案 1 :(得分:0)

我会选择你自己的解决方案,并以Apache Apache Commons Transaction Algorithm给出的算法为出发点。 你可以使用ReentrantReadWriteLock,虽然这个锁通常更适合一个生产者的模式 - 许多读者的情况,这可能不是你想要的。看起来你的锁更像是一个普通的可重入互斥锁。