サブツリーを書き込み用にロックすると、構造全体がロックされるとおっしゃっていますが、あなたの質問をよく理解したかどうかはわかりません。したがって、単純な解決策は、構造全体に対して1つのRWロックを設定することです。
ちなみに、java.util.concurrent.atomic
RWロックのツリー以上の助けにはなりません。
兄弟を独立してロックできるようにしたい場合は、2番目の解決策(各ノードがその親への参照を持つロックのツリー)を使用できます。
ノードをロックすると、書き込みロックを使用してノードがロックされ、読み取りロックを使用してすべての親がロックされます。子がすでに読み取りロックを取得しているため、子をロックすると書き込みロックを取得できないため、子がロックされている間は親をロックできません。子のロックは、他のスレッドが親を書き込みロックしていない場合にのみ許可されます。
上記のロックは専用ロックです。
(読み取り/書き込みロックの別名は共有専用ロックです)
共有ロックを追加するには、各ノードに次のことを示すアトミック整数も必要です。正の場合、間接的な書き込みロックされた子の数。負の場合、ノードがリードロックされた回数。また、ノードとその親は、親で新しい書き込みロックが取得されないように、読み取りロックされます。
擬似コード:
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;
}
ロックを取得できなかった場合は、ロックしたもののロックを解除し、後で再試行します(これを実現するには、グローバル条件変数、タイムアウトなどを使用できます)。
編集:ツリーを読み取りロック/書き込みロックするためのコードを追加しました