(回転)原理とソースコード解析ReentrantLockの

背景:ReetrantLock AQSは、基本となる実装(CAS + CHL)に基づいており、公正かつ不当な2つの違いがあります。

この基本的なメカニズムは、それが元を追跡することによって、分析のために必要です。

参照

ReentrantLockの原理とソースコード解析

ソースコード解析

次に、我々はそれが再入を確保するためですが、また、公正なロックを実現する方法をどのようにの原則のソースReentrantLockの実現の観点から見てください。

  ReentrantLockのは、AQSに基づいており、AQSは、Javaとint型の状態変数と、そのような待ち行列スレッドとして、共有リソースを取得するためのFIFOキューによって状態であり、多くの同期コンポーネントの基盤を構築する契約です。AQSはボトムフレームワーク、ブロッキングようなスレッドをキューイングなどの一般的な、より複雑な論理骨格を、定義するテンプレート方法は、ウェイクアップ、抽出されたこれらの複合体が、実質的な部分の一般的であり、これらは、同期コンポーネントを構築する必要がありますユーザーは、ユーザーが唯一のあなたは(いくつかの簡単な操作の放出を得るために共有変数の状態のため、実際には)を指定することができますいくつかの簡単な方法を書き換える必要があり、介護を必要としません。

  AQSで上記の簡単な紹介は、詳細は私の他の記事を参照してください「Javaと礎石は、詳細な契約を-AQS」ここでそれらを繰り返すことはしません。いくつかの一般的に用いられている方法ではまず見て、我々はトップダウンから押し込みます。

引数のないコンストラクタ(デフォルト以外のロックフェア)

ReentrantLockの公開(){ 
        同期新しい新NonfairSync =(); //デフォルトの非公正
    }

同期は、ReentrantLockの内部同期コンポーネントが達成され、それはAQSから継承し、ReentrantLockのの静的内部クラスで、我々は後に分析します。

  ブール値(公正かどうか)を持つコンストラクタ

ReentrantLockの公開(ブールフェア)の{ 
        SYNC =フェア新しい新しいFairSync():?新しい新しいNonfairSync(); trueに//フェア、フェアロック;逆に、不当ロック
    }

ここであなたは、公正なロックを採用するかどうかを指定することができます参照してください、FailSyncおよび静的内部クラスNonFailSyncはReentrantLockのも、私たちは同期から継承されてきました

概要

  実際には、上記の方法から、この導入を書くために、私たちすることができますおそらくソート三つの重要な内部の静的内部クラス、同期、NonFairSync、FairSyncを定義する処理ロジックReentrantLockの、アウト。NonFairSyncとFairSyncは、継承Syncは、一般的な論理Syncを呼び出して、その後、その中に、AQS継承共通してReentrantLockの同期同期コンポーネント、(トップレベルの複雑なロジックまあ、スレッドキュー、ブロックする、などの利点を取るためにAQSは、目を覚ます)としてその具体的な内部ロジック(または公正公平を)完了します。

 NonFairSync(非リエントラントロックフェア)

コードをコピー
静的最終的なクラスNonfairSyncは同期{//継承同期延び
        、プライベートロングのserialVersionUID =最終静的7316153563782823691L 
        / **取得ロック* / 
        最終ボイドロック(){ 
            IF(compareAndSetState(0 ,. 1))//セット状態CAS状態、元の場合を値は0、それが1に設定されている
                //現在のスレッドがロックを保持しているとしてマークされている。setExclusiveOwnerThread(にThread.currentThread())
            他の
                取得(1); // AQSのセットアップが失敗した場合、コール・取得方法、意志の取得我々は次のメソッドを書き換えるtryAcquireを呼び出します。1つの現在のスレッドがリソース、状態0に到達しませんが、他のスレッドがリソースをつかむために0から1までの状態は、状態はCASの故障で、その結果、改訂された; 2状態:ここでは、コールが失敗したことを、2つのケースがあります原稿があること、0ではない、スレッドを取得するためのリソースを持って、リソースを取得するために他のスレッドがある可能性があり、そこに、現在のスレッドによって得られたスレッドは、次に取得するために繰り返されるので、我々がすべきnonfairTryAcquireでtryAcquireを行くすることができますあなたは、論理リエントラントの実現を見ることができます。
        } 
        } 
        tryAcquireに保護最終ブール(INT取得){
            リターンnonfairTryAcquire(取得);メソッド同期を呼び出す// 
    }
コードをコピー

 

nonfairTryAcquire()

コードをコピー
ブールnonfairTryAcquire決勝(int型買収){ 
            決勝にThread.currentThreadスレッド電流=(); //現在のスレッドを取得する
            int型、C = getStateを(); //現在の状態値を取得する
            (C == 0){//場合の状態が0である場合取得リソースへのスレッドが、CASの意志状態が1に設定されていないことを意味し、現在のスレッドIは排他ロックスレッドに取得トークン、真へ戻る
                IF(compareAndSetState(0、取得)){ 
                    setExclusiveOwnerThread(電流); 
                    真への復帰; 
                } 
            } 
            そうIF(現在getExclusiveOwnerThread ==()){//状態がゼロではなく、現在のスレッドは、ロック糸保持場合
                INT nextc = C +取得し; //状態が累積1。
                IF(nEXTC <0)// INTタイプオーバーフロー
                    スロー新しいエラー(「最大ロック数が超過」);
                SETSTATE(nextc); //セット状態、このときの状態が1よりも大きい場合、複数のスレッドのロック状態リエントラントスレッドの値の数である対象、で表す
                真戻り、戻り真//、正常ロック獲得
            } 
            偽に戻ります。 //ロックが失敗します
        }
コードをコピー

 

単純な集計処理:(PS:ロックを取得する処理、ロックは、実装プロセスで共有されます)

    1.まず、状態値がこの時点で取得リソースへのスレッドが存在しないことを意味し、0であり得る、CASは1に設定し、正常に排他ロックを取得したセットを表します。

    2.状態が0より大きい場合、スレッドが存在しなければならないケースである、プリエンプションが自分であるかどうかを決定するために再度スレッドリエントラントの数である状態の累積的な状態、trueを返し、成功した再突入、値をリソースをつかむ、この時間ばなりません;

    3.その他の場合、ロックの取得に失敗。

  外観フェアリエントラントロック処理ロジック

  FairSync

コードをコピー
最終的なクラスFairSyncは同期静的{延び
        プライベートロングのserialVersionUID =最終的な静的-3000897897090466540L; 

        最終ボイドロック(){ 
            獲得(1); //テンプレートメソッドを直接取得をAQS呼び出し、取得以下我々はtryAcquireに書き換え、これを呼ぶ
        } 

        最終保護しますtryAcquireブール(int型の取得){に
            決勝にThread.currentThreadスレッド現在の=(); //現在のスレッドを取得する
            int型、C = getStateを(); //状態値を取得する
            場合(C == 0){//状態が0、手段である場合現在のスレッドがリソースに取得していない、それはまだ直接的なリソースを得ることができますか?NO!これは論理的なものとして、ロック直前に不公平ではありません。次のロジックを参照してください。
                現在のスレッドが排他ロックを保持している(!のHasQueuedPredecessors()&& //裁判官あれば取得するために何のために存在しない場合、アプリケーションが、自分のスレッドの前の行にロックされているかどうかを、年代順に、CASのセット状態、およびマークスレッド、そうでない場合、すなわち、それは公正な治療!得ることができない
                    compareAndSetState(0、取得し)){
                    setExclusiveOwnerThread(現在); 
                    真への復帰; 
                } 
            } 
            そうIF(現在getExclusiveOwnerThread ==()){//処理ロジックの再進入、上記と一致は、省略する
                INT NEXTC = C +取得し、
                IF(NEXTC <0)
                    新しい新しいエラー投( "最大の数を超えてロックをCOUNT"); 
                SETSTATE(NEXTC); 
                trueに復帰; 
            } 
            falseに復帰; 
        } 
    }
コードをコピー

 あなたが公正かつ非論理ロック通常、公正ロックは同じですが、見ることができ、違いは、この判決の論理)(!のHasQueuedPredecessorsで、状態はゼロであったとしても、私たちは急いで最初にもそこを参照して、買収に直接行くことができないということです後で処理を行って取得しようとするキュー内のスレッド、そうでない場合は、。一方、偽の、取得失敗を返します。

  キュー内の論理スレッドがあるかどうかを判断するために、このを見てください

  hasQueuedPredecessors()

コードをコピー
最終的なブールhasQueuedPredecessors公開(){ 
        ノードT =尾; //エンドノード
        ノードH =ヘッド; //ノードヘッド
        ノードS; 
        !H = Tリターン&& 
            ((h.next = S)S == NULL || !.thread =にThread.currentThread()); // 先に自身のスレッドの存在かどうかを判断します
    }
コードをコピー

 trueを返します:それは2例がある、というコードで、前に少し周りのロジックの独自のスレッドでそこに来たかどうか、この判断は、我々は次の整理する必要があることに留意すべきでtrueが返されます、我々はこの分解ロジック(注を見てそこに他のスレッドがあり、彼らがつかむために放棄する必要があるよりも早くアプリケーションをロックすることを意味します)

  1  H!= T &&(S = h.next)== NULL、このロジックは、ヘッドノードを指し示す一つのヘッドを設定することができる、このときの尾はヌルです。ロックを取得するためにいくつかの他のスレッドが失敗した場合(同期キューが空であると仮定して)、参加ノードの同期キューを構築する必要性を、追加したとき、あなたは(無意味な人形のヘッドノードを作成する必要があります。このシナリオを考えてみましょうスピンCAS操作であるAQS ENQ方法)においては、ノードの完了後、このダミーヘッドをポイントすることが可能であり、尾部がこのノードに向けられていません。明らかに、これは、良い時間のスレッド上で現在のスレッドよりもあるので、そこにスレッドを待っていると、自分よりも早くにも来ていることを示す、trueを返します。

  2. H!= T &&(S = h.next)!= NULL && s.thread!=にThread.currentThread() いくつかの同期キューイングスレッドキューと現在のスレッドが第二子ノードではありません、この状況はtrueを返しますがありました。何s.thread!=にThread.currentThread()裁判官が存在しない場合は、何が起こるのだろうか?現在のスレッドがすでに同期キューに(最初のノードと、これは無意味な人形ノードた)2番目の子ノードである場合には、彼女の2番目の子ノードのスレッドを起こして、2番目のノードをロック解除リソースを保持しているスレッドtryAcquire再スレッドが(このロジックは、AQSのメソッドをacquireQueued)、この戻り値がtrueであるかどうかを判断するためにs.threadなし!=にThread.currentThread()、hasQueuedPredecessorsに原因tryAcquire失敗を呼び出します。

PS:単語の前に、現在のスレッドをチェックするスレッドが待っています

  最後に、同期に定義されてReentrantLockのtryReleaseを見て、取ります

コードをコピー
最終的なブールtryReleaseは(int型リリース){保護された
            int型getStateをC =() -リリース; //マイナス1つのリソース
            IF(!にThread.currentThread()= getExclusiveOwnerThread())
                スロー新しい新は、IllegalMonitorStateException(); 
            falseにブール=無料; 
            / / 0の状態値が現在のスレッドが完全にtrueを返し、クリーンリリースされていることを示している場合、上側のAQSは、リソースが空いてきたことを理解するであろう。そうでない場合は0、その後、スレッドはまだリソースを持っていますが、それは資源の再入国をリリースする予定、偽を返します。
            IF(C == 0){ 
                無料= trueに; // 
                setExclusiveOwnerThread(NULL); 
            } 
            SETSTATE(C); 
            戻り無料; 
        }
コードをコピー

 

概要

リエントラントをReentrantLockの、公平性がミューテックスを実装することができる、それはAQSのフレームワークに基づいて設計されて、再入及び公平ロジックが理解することは困難ではない実施、毎回再エントリは、状態が1だけインクリメントされ、もちろん、リリースされたとき、彼らは層によって層を解放する必要があります。公平性のためとして、1人のより多くの裁判官がロックを取得しようとすると:もしそうなら、早くキュー内で待機しているスレッドの同期であるかどうか、より多くの彼らのアプリケーションよりも待たなければ、そうでない場合は、押収を許可します。

 

ReentrantLockの原理

PS:このブログはそれについて話して、よりユーザーフレンドリー

FIFOキューを使用してAQSがロックを待機中のスレッド、キューヘッド「センチネルノード」と呼ばれるノードまたは「ダムノード」のキューを表し、それがどのスレッドに関連付けられていません。待機中のスレッドに関連付けられた他のノードは、各ノードは、待機状態を維持waitStatus

ロックを取得するためにCASの試みで最初:ReentrantLockのは、基本的には、のように要約することができます。この時点でスレッドがすでにロックが占有されている場合は、キューに参加し、AQSは中断されます。ロックが解除されると、チームはキューCLHスレッドで1位目覚めされ、その後、CASは再びロックを取得しようとします。このとき、もし:

不公平ロック:あなたが取得するために別のスレッドの試みがあると同時に、入って来た場合、それは先に得られ、このスレッドを行うことが可能です。

フェアロック:あなたが取得するために別のスレッドの試みがあると同時に、入って来た場合、彼らがチームの最初の言葉ではないことが判明したとき、チームはキューの先頭には、スレッドのロックを取得するには、末尾にルーティングされます。(差分)

リエントラントロック。リエントラントロックは、同じスレッドが同じロックを複数回取得できることを意味します。ReentrantLockのは同期とリエントラントロックされています。

割り込み可能ロック。割り込みロックが割込みに応答することができる、でロックを取得するために、プロセススレッドの試みを指します。同期ロックが中断しないで、そしてReentrantLockの割り込み機能を提供します。

フェアロックと非ロックフェア。複数のスレッドが同時にスレッドを達成するためにロックを得るために、同じロックを取得しようとするのではなく、公正なロック・スレッドがに許可されていることをフェアロック手段「キューをジャンプします。」公正かつ非同期ロック、及び非株式ReentrantLockのロックのデフォルトの実装が、またロック公正に設定することができます。

ロック()

1.最初のステップ。ロックを取得しようとします。あなたがロックを取得しようとすると成功すると、この方法は、直接返します。

チームへ2.第二段階、。上記Aのスレッドロックに既に占有するので、B及びCにtryAcquireが失敗行い、待ち行列。スレッドがロックA死んだリンクを保持する場合は、BとCは中断されます。

3.第3のステップは、ハングアップすることです。B及びCを順次acquireQueued行う(最終ノードノード、INT引数)されています。このアプローチは、スレッドは、障害が中断される場合は、ロックを取得しようとするにチームを聞かせています。

チームへのスレッドがその前身のノードの状態が信号であることを前提にハングアップすることができた後、その意味は「兄弟の前に、こんにちは、あなたはチームのロックアウトされた場合は、私を目覚めことを忘れない!」です shouldParkAfterFailedAcquireは最初、彼らは真のリターンを満たしていれば、現在のノードの前駆体の状態は、要件を満たすかどうかを決定しますので、その後、parkAndCheckInterruptを呼び出し、自分自身をハングします。見た目前駆体ノードがある、ない場合は> 0(中止)、あなたは、前駆体ノードステータスがSIGNALに設定されますされていない場合、前駆体の要件を満たすために最初まで前方横断している場合。

 状態はSIGNALノード前駆体ではない場合、プロセス全体を通じて、そしてあなたの心の自身の平和は、同時にあなたは競合ロックしようとする機会がないかどうかを確認しようとすることができ、安心ポイントを見つける必要保留、中断することはできません。

    ファイナリティキューは、以下に示すことができるように

コードをコピー
最終ノードクラス{静的
        / ** waitStatus値は、スレッドが* /(又は中断待ちタイムアウト)キャンセルされたことを示す
        。静的INTがキャンセル=ファイナル1 
        / ** waitStatus値が後続のスレッドのニーズは(unpaking)* /覚醒することを示し、
        静的int型= SIGNAL -1決勝; 
        / ** waitStatus値信号は、キューに同期キューからシフトするとき、条件条件を待っているスレッド上のノードを示す* / 
        スレッドを待っ示すために/ ** waitStatus値はオンになっています*条件条件の/ 
        静的INT最終状態= -2; 
       / **発現無条件ダウン伝播される共有同期状態waitStatus値、
        伝搬最終INT静的= -3; 
        / **待機状態、* 0 /最初に
        揮発性waitStatusのint; 
        / **現在ノード前駆ノード* / 
        揮発性PREVノード; 
        / **現在ノードの後続ノード* / 
        揮発次ノードと、
        スレッド* /内の現在のポイントに関連付けられている/ **キューイングノード
        揮発性スレッドthread; 
        / ** ... * / 
    }
コードをコピー

ロック解除()

あなたは、プロセスのロックを理解していれば、その後、はるかに簡単と思われるロックを解除します。プロセスは、一般的に最初のリリースが成功した場合、ヘッドノードの状態が信号である場合、ヘッドノードは、その後のリリースがロック解除の失敗にfalseを返すことができなかったのであれば、スレッドに関連付けられている次のノードをウェイクアップされている場合、その後、参照、ロックを解除してみてください。ここでは、各時間しか想起ノード次のスレッドの最初のノードに関連付けられていることが分かりました。

 不当ロックロック取得処理を要約するフローチャート。 

 

 


あなたはあなたが助けにこの記事を読んでいると感じた場合は、「クリックしてくださいお勧めしますより多くの人々が幸福の取得の知識を楽しむことができるように、ボタンを」!私は職場に入っていますので、自分の限られた経験の観点から、そう、このブログの内容のほとんどは、ネットワークの既存の知識のまとめから来て、再版にコメントを歓迎し、我々は進歩一緒に勉強します!侵害した場合、私に連絡し、かつ効果的にあなたの権利を保護してください!

おすすめ

転載: www.cnblogs.com/eryun/p/12044610.html