10

ReadWriteLockReentrantReadWriteLockダウングレードは実装によって許可されています (tryLock()以下の例では常に が返されますtrue):

void downgrade(final ReadWriteLock readWriteLock) {
    boolean downgraded = false;
    readWriteLock.writeLock().lock();
    try {
        // Always true, as we already hold a W lock.
        final boolean readLockAcquired = readWriteLock.readLock().tryLock();
        if (readLockAcquired) {
            // Now holding both a R and a W lock.
            assert ((ReentrantReadWriteLock) readWriteLock).getReadHoldCount() == 1;
            assert ((ReentrantReadWriteLock) readWriteLock).getWriteHoldCount() == 1;

            readWriteLock.writeLock().unlock();
            downgraded = true;
            try {
                // Now do some work with only a R lock held
            } finally {
                readWriteLock.readLock().unlock();

                assert ((ReentrantReadWriteLock) readWriteLock).getReadHoldCount() == 0;
                assert ((ReentrantReadWriteLock) readWriteLock).getWriteHoldCount() == 0;
            }
        }
    } finally {
        if (!downgraded) {
            // Never (we were holding a W lock while trying a R lock).
            readWriteLock.writeLock().unlock();
        }
        assert ((ReentrantReadWriteLock) readWriteLock).getReadHoldCount() == 0;
        assert ((ReentrantReadWriteLock) readWriteLock).getWriteHoldCount() == 0;
    }
}

同様の方法でロックのアップグレードを許可しない理由は何ですか? 以下の書き込みロックのtryLock()メソッドは、読み取りロックを保持している他のスレッドがない場合、デッドロックのリスクなしで安全に戻ることができます。true

void upgrade(final ReadWriteLock readWriteLock) {
    readWriteLock.readLock().lock();
    try {
        // Always false: lock upgrade is not allowed
        final boolean writeLockAcquired = readWriteLock.writeLock().tryLock();
        // ...
    } finally {
        readWriteLock.readLock().unlock();
    }
}
4

1 に答える 1

6

まず、アップグレードとダウングレードは、 の意味的な複雑さの点で同等ではないことに注意してくださいReadWriteLock

ダウングレード トランザクションを完了するために競合を回避する必要はありません。これは、ロックに対して最もエスカレートされた特権を既に保持しており、現在ダウングレードを実行している唯一のスレッドであることが保証されているためです。同じことがアップグレードには当てはまらないため、アップグレードをサポートするメカニズムは当然、より複雑にする (またはよりスマートにする) 必要があります。

使用可能にするために、アップグレード メカニズムは、2 つの読み取りスレッドが同時にアップグレードを試みた場合 (特に、ReentrantReadWriteLock複数の読み取りロックを保持している単一の読み取りスレッドがアップグレードを試みた場合) にデッドロックを防止する必要があります。さらに、失敗したアップグレード要求をどのように処理するか (その読み取りロックを無効にするか) をメカニズムで指定する必要があり、それはさらに重要です。

お気づきかもしれませんが、これらの問題を完全に処理するReentrantReadWriteLockことは、控えめに言っても不便です (それでも、これは .NET がReaderWriterLock試みていることであり、私は実際に成功していると思います)。私の推測ではfinal boolean writeLockAcquired = readWriteLock.writeLock().tryLock();、いくつかの些細なケースでは成功するように作られていましたが、アップグレード可能性は一般的な使用にはまだ十分ではなかったでしょう.十分に激しい競合の下で、書き込みロックの競争に負けた場合、あなたは同じです読み取りロックのロックを解除し、書き込みロックを取得しようとしたかのようにボートします (他の誰かが侵入して書き込みロックを取得する機会を残します)。

ロックのアップグレード可能性を提供する良い方法は、1 つのスレッドのみがアップグレードを試行できるようにすることです。これがReentrantReadWriteUpdateLock、.NET のReaderWriterLockSlim機能です。ただし、Java 8 を次のようにお勧めしますStampedLock

  • 競合が少ない場合、その楽観的な読み取りは、読み取りロックを使用するよりもはるかに高速です
  • その API は、アップグレードに関する制限がはるかに少ない (楽観的な読み取りから読み取りロック、書き込みロックへ)
  • 現実的な JMH ベンチマークを作成するための私のさまざまな試みは、他の同様のロックの 1 つがそれを打ち負かし、ほとんどの場合失敗しました。
于 2016-04-11T23:09:00.177 に答える