次のエディターは、ReadWriteLock インターフェイスとその実装 ReentrantReadWriteLock メソッドに関する記事を提供します。編集者はこれがとても良いものだと思ったので、皆さんの参考として今から共有します。エディターに従って見てみましょう
Java 同時実行パッケージのロック パッケージのロックが基本的に導入されました。シンクロナイザー AQS の動作メカニズムを明確に理解すると、実際には非常に簡単になります。この章では、もう 1 つの重要なロックである ReentrantReadWriteLock 読み取り/書き込みロックに焦点を当てます。
ReentrantLock は排他的ロックです。これは、ロックを 1 つのスレッドによってのみ取得できることを意味します。しかし、スレッドが読み取り操作のみを実行するシナリオの場合はどうなるでしょうか。このように、ReentrantLock は、読み取りスレッドのセキュリティを確保する必要がなく、この方法でのみパフォーマンスと効率を最大限に保証できます。 ReentrantReadWriteLock は、読み取りロックと書き込みロックに分割されており、書き込みロックを取得できるのは 1 つの書き込み操作スレッドだけです。 lock は共有ロック (AQS の共有モード)、読み取りロックは排他ロック (AQS の排他モード) です。まず、読み取り/書き込みロックのインターフェイス クラスを見てみましょう: public interface ReadWriteLock {
Lock readLock(); //获取读锁
Lock writeLock(); //获取写锁
}
ReentrantLock と同様に、ReentrantReadWriteLock も内部クラス Sync を通じてシンクロナイザー AQS を実装します。この点の考え方は ReentrantLock と似ています。 ReadWriteLock インターフェイスで取得される読み取りロックと書き込みロックはどのように実装されますか?
//ReentrantReadWriteLock private final ReentrantReadWriteLock.ReadLock readerLock; private final ReentrantReadWriteLock.WriteLock writerLock; final Sync sync; public ReentrantReadWriteLock(){ this(false); //默认非公平锁 } public ReentrantReadWriteLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); //锁类型(公平/非公平) readerLock = new ReadLock(this); //构造读锁 writerLock = new WriteLock(this); //构造写锁 } …… public ReentrantReadWriteLock.WriteLock writeLock0{return writerLock;} public ReentrantReadWriteLock.ReadLock readLock0{return ReaderLock;}
//ReentrantReadWriteLock$ReadLock public static class ReadLock implements Lock { protected ReadLock(ReentrantReadwritLock lock) { sync = lock.sync; //最后还是通过Sync内部类实现锁 } …… //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等 }
//ReentrantReadWriteLock$WriteLock public static class WriteLock implemnts Lock { protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } …… //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等 }
上記は ReentrantReadWriteLock の概要です。実際には、その中に ReadLock と WriteLock という 2 つのロックがあることがわかります。これは、セルフロック インターフェイスを実装しており、ReentrantLock と比較できます。これら 2 つのロックの内部実装は、シンクロナイザー AQS である Sync を通じて実装されます。これは、ReentrantLock の Sync と比較することもできます。
キュー
で、もう 1 つは同期 ステータス です。この同期ステータスは、読み取り/書き込みステータスである読み取り/書き込みロックに適用されます。ただし、AQS では、同期ステータスを表す状態整数のみが存在します。読み取り/書き込みロックでは、記録する必要がある読み取りと書き込みの 2 つの同期ステータスがあります。したがって、読み取り/書き込みロックは AQS で状態整数を処理します。これは合計 4 バイトと 32 ビットの int 変数であり、読み取り状態と書き込み状態はそれぞれ 16 ビットを占有します。上位 16 ビットは読み取りを表します。 16 ビットは書き込みを示します。
ここで質問があります。state の値が 5 の場合、バイナリは (00000000000000000000000000000101) になります。これには、変位操作を使用する必要があります。計算方法は、書き込み状態 state & 0x0000FFFF、読み取り状態 state >>> 16 です。書き込み状態を 1 増やすことは状態 + 1 に等しく、読み取り状態を 1 増やすことは状態 + (1 < < 16) に等しくなります。ビットシフト演算
については、『<<、>>、>>>シフト演算』を参照してください。 書き込みロックの取得と解放
これまでの経験に基づいて、AQS はロックを取得するためのアルゴリズム スケルトンをすでに設定しており、tryAcquire (排他的ロック) を実装するためにサブクラスのみが必要であることがわかります。 tryAcquire を確認します。
//ReentrantReadWriteLock$Sync protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread; int c = getState(); //获取state状态 int w = exclusiveCount(c); //获取写状态,即 state & 0x00001111 if (c != 0) { //存在同步状态(读或写),作下一步判断 if (w == 0 || current != getExclusiveOwnerThread()) //写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败 return false; if (w + exclusiveCount(acquire) > MAX_COUNT) //锁重入是否超过限制 throw new Error(“Maxium lock count exceeded”); setState(c + acquire); //记录锁状态 return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; //writerShouldBlock对于非公平锁总是返回false,对于公平锁则判断同步队列中是否有前驱节点 setExclusiveOwnerThread(current); return true; }
//ReentrantReadWriteLock$FairSync final boolean writerShouldBlock() { return hasQueuedPredecessors(); }
理由はなぜですか?これは、不公平なロックと公正なロックの違いに戻ります。詳細については、「5. ロック インターフェイスとその実装 ReentrantLock」を参照してください。不公平なロックの場合、スレッドはロックを取得するたびに、同期キューにスレッドがあるかどうかに関係なく、まずロックの取得操作を強制します。取得できない場合、スレッドはキューの最後まで構築されます。同期キューが存在する限り、公平なロックの場合。キュー内にスレッドがある場合、ロックは取得されませんが、スレッド構造はキューの最後に追加されます。書き込みステータスの取得に戻ります。tryAcquire メソッドでは、ロックを保持しているスレッドがないことがわかりましたが、この時点では、不公平なロックの場合はロックの取得、公正なロックの場合は、対応する操作が異なるロックに従って実行されます。 - 同期キュー スレッド内にスレッドがあり、ロックの取得がなく、キューの最後に追加されます。
書き込みロックの解放プロセスは、基本的に ReentrantLock の解放プロセスと似ています。結局のところ、これらはすべて排他ロックであり、書き込みロックが完全に解放されたことを意味する 0 になるまで、書き込みステータスが減少します。
リードロックの取得と解除同様に、これまでの経験に基づいて、AQS はロックを取得するためのアルゴリズム スケルトンをすでに設定しており、サブクラスで tryAcquireShared (共有ロック) を実装するだけで十分であることがわかります。そのため、tryAcquireShared を確認するだけで済みます。共有モードのロックでは、複数のスレッドが同時にロックを取得できることがわかっています。ここで、T1 スレッドがロックを取得し、このとき、T2 も同期状態を取得します。 lock、state=2、次に T1 スレッド Re-entry state = 3。これは、読み取り状態がすべてのスレッドの読み取りロック数と、各スレッドが読み取りロックを取得した回数の合計であることを意味します。 ThreadLock にのみ保存され、スレッド自体によって維持されるため、ここでいくつかの複雑な処理を実行する必要があり、ソース コードは少し長くなりますが、複雑なのは各スレッドが取得する回数を保存するという事実にあります。詳細については、ソース コードの tryAcquireShared を参照して、上記の書き込みロックの取得の分析と組み合わせて理解することは難しくありません。
読み取りロックの解放で注目すべき点は、それ自体が維持するロック取得の数と、シフト操作による状態の減少です (1
以上がJAVA の ReadWriteLock インターフェイスとその実装 ReentrantReadWriteLock メソッドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。