以下是我正在考虑的想法:
>使用Apache Commons Transaction.不幸的是,该项目自2008年3月以来一直没有更新,并且已经非正式终止.一些API文档似乎表明即将推出的版本(1.3或2.0)将包括some kind of hierarchical locking,但是无法找到源代码,似乎我们无法再访问其SVN存储库.
>使用一系列ReentrantReadWriteLock
s,我将按层次结构组织.我不是并发专家,而且我有点害怕自己这样做.初步的想法似乎表明,即使在我尝试锁定子树之前,我必须在管理ReentrantReadWriteLocks本身的整个结构上使用外部锁 – 这样即使释放锁,我也必须使用外部锁…
>使用java.util.concurrent
和java.util.concurrent.atomic
中的类以比使用一系列ReentrantReadWriteLocks更有效的方式实现我的分层锁定.
我已经准备好走最后一条路,但我很惊讶没有找到任何可以更好地解决这个问题的现有库.所以:
解决方法
因此,简单的解决方案是为整个结构设置一个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; }