Javaマルチスレッド-ReentrantReadWriteLockソース読み


前に「AQSソース読解」と「ReentrantLockソース読解」について話しましたが、今回はReentrantReadWriteLockを読み続け、前の2つの文章を見たことがないことをお勧めします.これはReentrantLockの簡単なアップグレードではなく、着地シーンの最適化です.詳しく見てみましょう.

背景


JUCバッグにはすでにReentrantLockが入っていますが、なぜReentrantReadWriteLockが必要なのでしょうか.頭の注釈を見て手がかりを探す.ReadWriteLockインタフェースの実装です.では、このインタフェースを見てみましょう.実際のシーンでは、一般的には、データを書くよりもデータを読むほうがはるかに多いです.スレッドが安全でないことを回避するために、スレッドを独占ロックでロックすると、非常に非効率であり、同時性も失われます.マルチスレッドも意味がありません.だからReadWriteLockはこの問題を解決するために存在します.
ReentrantReadWriteLockのヘッダ注記を参照してください.ReentrantReadWriteLockは依然として公平ロック/非公平ロックの機能を持っており、ReentrantLockとは異なり、前者の内部ではリードロックとライトロックが維持されており、公平/非公平モードでは、彼らは一緒にこのロックリソースを競争します.上図は2つのReentrantReadWriteLockの最も核心的なルールです.
  • はリードロックを申請します.
  • に成功するには、他の書き込みロックが占有されていないか、リードロックがキューに並んでいる時間が最も長い場合です.
  • 書き込みロックを申請します.他のスレッドがリード/ライトロックを占有していない場合、
  • に成功する.
    また、以上の2つのルールは、
  • 書き込みロックは、読み取りロックよりも高い
  • リードロック占有率がある場合は引き続きリードロックを申請することができるが、他のスレッドはライトロック
  • を申請することができない.
  • 書き込みロックがある他のスレッドの読み書きを占有しても
  • を申請できない.
    したがってReadWriteLockインタフェースの説明を引くことで,読み取りと書き込みを独占させ,プログラムの同時性を向上させることができる.

    ReentrantReadWriteLock構成


    ReentrantReadWriteLockのfile strutureを見る前にReentrantLockのソースコードを見たことがある学生は、この構造をよく知っているに違いありません.同じように見えるのはSync同期器(AQSのサブクラス)と、その2つの公平/非公平サブクラスです.
    異なるのは、ReadLock内部クラスとWriteLock内部クラス、および対応するメンバー変数とメソッドを読み書きすることです.さらにlock()やunlock()などの方法は少なく,ロック解除機能の下にこの2つのサブクラスを与え,ReadWriteLockインタフェースの定義に合致する.

    Sync内部クラス


    ReentrantReadWriteLockとReentrantLockにはSyncがありますが、実際にはSyncの方法は大きく異なります.Syncの構造比較を見ると、以前のReentrantLockのSyncとは、共有ロックの取得と解放に使用される**shared()メソッドが多くなっています.またtryReadLock()やtryWriteLock()はWriteLockやReadLock内部クラスに使用されます.

    tryAcquire()排他ロック(書き込みロック)申請


    再入ロックは、stateが再入の回数を表し、読み書きロックの意味でstateが表す読み書き占有(再入)の回数について説明した.cはstate,wは独占再入回数である.スレッドがロックを占有している場合(c!=0)、書き込みロックがない場合(w=0)、または独占スレッドが現在のスレッドではない場合、false取得に失敗します.ロックの再入総数が上限を超えると異常が放出されます.ここでは、ロックが占有されている場合、ロックを読むだけであれば、申請に成功することが容易にわかります.これがリードロックのロックアップグレードです.スレッド占有がない場合、writerShouldBlock()を実行してスレッドをブロックする必要があるかどうかを判断し(サブクラスが独自の条件を実現する)、必要でない場合はCAS state値を返し、成功を返す.

    tryAquireShared()共有ロック(リードロック)申請


    読み込みロック申請は書き込みロック申請よりも複雑で、接触していないメンバー変数が多く、判断文も多い.まずメンバー変数を見て、それぞれの変数の注釈からわかります.
  • firstReaderは、リードロックを取得する最初のスレッド
  • である.
  • firstReader HoldCountは、firstReaderのカウンタです.
  • cachedHoldCounterは、最近、リードロックの取得に成功したスレッド保有数カウンタです.
  • readHolds、現在のスレッドの読み込みロック回数.ThreadLocalは、スレッドが安全なHoldCounterです.

  • 書き込みロックが占有されているかどうかを判断し、書き込みロックが現在のスレッドでない場合、読み取りロックの取得に失敗し、メソッドを終了します.書き込みロックが現在のスレッドであれば、書き込みロックが独占的であるため、他のスレッドとデータを共有する問題はありません.
    サブクラス条件を満たし、総数を超えず、CASも成功した場合、リードロックがなければfirstReaderを現在のスレッド、firstReader HoldCountを1とする.リードロックがあり、現在のスレッド申請でもある場合、firstReader HoldCountは1から増加します.リードロックがあり、現在のスレッド申請ではない場合は、成功したキャッシュカウンタを取り、このカウンタが現在のスレッドではない場合は、現在のカウンタに設定し、自己増加し、成功を返します.(実はキャッシュカウンタを現在のスレッドに置き換えたカウンタ)最後に条件を満たさなかったりCASに失敗したりしてfullTryAcquireShared(current)戻りを実行します.これらのデータは何をしているのか、後でrelease()の使い方を見てみましょう.実はこの方法はforループポーリングでCASの紛失と再入の失敗の問題を解決することで、具体的なコードは細かくなくて、興味があれば自分でソースコードを探して見ることができます.

    tryRelease()排他ロック(書き込みロック)解放


    ここにまたコンディションの跡があり、コンディション時にロックの動作を制御したり、モーニングコールをキャンセルしたりすることができるかもしれません.また、ロックは読み取りロックと書き込みロックを同時に解放します.この方法は,現在のスレッド操作であれば,再入数から解放数を0と減算すれば解放できることが理解できるが,そうでなければ失敗する.

    tryReleaseShared()共有ロック(リードロック)解除


    読み込みロックを解除すると、読み込み中のスレッドには影響しませんが、待機している書き込みスレッドに書き込みロックの取得を開始させることができます.残りはtryAquireShared()で計算されたcount値を解放(自減)し、最終的に自減が0の場合はリードロックを解放することに成功した.

    WriteLock、ReadLock内部クラス


    前述のReentrantReadWriteLockのlock()、unlock()操作は、Write/ReadLockに割り当てて実行されます.彼らはロックインタフェースの実装なので、実際にはReentrantLockに最も似ているのはこの2つの内部クラスであるはずです.しかもほぼ変わらず、Syncの内部クラスを使っています.WriteLock、ReadLockの最大の違いはWriteLock用の独占モードの方法であり、ReadLock用は共有モードの方法である.具体的なコード実装は基本的に上で説明した構成であり,ReentranReadWriteLockの使用について説明する.ReentrantLockの場合は比較的簡単で、変数を宣言し、lock()メソッドを呼び出せばよい.
        ReentrantLock rl = new ReentrantLock();
        rl.lock();
        rl.unlock();

    しかしReentranReadWriteLockはLockインタフェースの実装ではないので、これらの方法はありません.あるのはwriteLock()やreadLock()だけで,このメソッドを呼び出して対応するロックオブジェクトを取得し,lock()を呼び出す.
         ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
         rwl.readLock().lock();
         rwl.readLock().unlock();
         rwl.writeLock().lock();
         rwl.writeLock().unlock();

    まとめ


    要点を振り返る
  • 読み書きロックReentrantReadWriteLockは、多読み少書きの実際のシーンに基づいて、同時性
  • を向上させる.
  • 読み書きロックのSync共有モードを追加する方法
  • 読み書きロックには、2つのオブジェクトreadLock、writeLockが内蔵されており、実際のロック解除
  • に使用されます.
  • 書き込みロックは独占的で、他のロックの申請
  • は許可されていません.
  • リードロックは同時に申請を繰り返すことができ、書き込みロックがある場合、ロックアップグレード
  • が発生する.
    悪くないと思うなら、公衆番号:Zack説コードに注目してください.