現在、コードでReentrantReadWriteLockを使用して、ツリーのような構造でアクセスを同期しています。この構造は大きく、一度に多くのスレッドによって読み取られ、その小さな部分が時々変更されるため、読み書きのイディオムにうまく適合しているようです。この特定のクラスでは、読み取りロックを書き込みロックに昇格できないことを理解しています。そのため、Javadocs に従って、書き込みロックを取得する前に読み取りロックを解放する必要があります。以前、再入不可のコンテキストでこのパターンをうまく使用したことがあります。
しかし、私が見つけたのは、永久にブロックしないと書き込みロックを確実に取得できないということです。読み取りロックは再入可能であり、実際にそのように使用しているため、単純なコード
lock.getReadLock().unlock();
lock.getWriteLock().lock()
再入可能に読み取りロックを取得した場合、ブロックできます。ロックを解除するための各呼び出しは、保持カウントを減らすだけであり、ロックは保持カウントがゼロになったときにのみ実際に解放されます。
最初はあまりうまく説明できなかったと思うので、これを明確にするために編集してください-このクラスには組み込みのロックエスカレーションがなく、読み取りロックを解放して書き込みロックを取得するだけでよいことを認識しています。私の問題は、他のスレッドが何をしているかに関係なく、このスレッドが再入可能に取得した場合、呼び出しgetReadLock().unlock()
が実際にこのgetWriteLock().lock()
スレッドのロックの保持を解放しない可能性があることです。その場合、このスレッドはまだ読み取りを保持しているため、への呼び出しは永久にブロックされますロックし、それ自体をブロックします。
たとえば、次のコード スニペットは、他のスレッドがロックにアクセスせずにシングルスレッドで実行されている場合でも、println ステートメントに到達しません。
final ReadWriteLock lock = new ReentrantReadWriteLock();
lock.getReadLock().lock();
// In real code we would go call other methods that end up calling back and
// thus locking again
lock.getReadLock().lock();
// Now we do some stuff and realise we need to write so try to escalate the
// lock as per the Javadocs and the above description
lock.getReadLock().unlock(); // Does not actually release the lock
lock.getWriteLock().lock(); // Blocks as some thread (this one!) holds read lock
System.out.println("Will never get here");
この状況を処理するための良いイディオムはありますか? 具体的には、読み取りロックを (おそらく再入可能に) 保持しているスレッドが、何らかの書き込みを行う必要があることを発見し、書き込みロックを取得するために独自の読み取りロックを「一時停止」したい場合 (他のスレッドで必要に応じてブロックして、読み取りロックの保持を解放し、その後同じ状態で読み取りロックの保持を「取得」しますか?
この ReadWriteLock の実装は再入可能になるように特別に設計されているため、ロックが再入可能に取得される可能性がある場合に、読み取りロックを書き込みロックに昇格させる賢明な方法は確かにありますか? これは、単純なアプローチが機能しないことを意味する重要な部分です。