CountDownLatchソースコード分析-CountDown()
前の記事では、ソースコードレベルからのCountDownLatchのawait()の原則について説明しました。この記事では、CountDown()について説明しています。
public void countdown(){// countdownlatch sync.releaseshared(1);}↓public final boolean leleseShared(int arg){// aqs if(tryreleaseshared(arg)){doreleaseshared(); trueを返します。 } return false;}↓保護されたブールントリレリーズシェア(int leases){//countdownlatch.sync // decrement count;ゼロに移行するときの信号(;;){int c = getState(); if(c == 0)falseを返します。 int nextc = c-1; if(compareandsetState(c、nextc))nextc == 0を返します。 }}コンストラクターCountDownLatch end = new CountDownLatch(2);状態は2に設定されているため、c == 2、nextc = 2-1、
次に、次のCAS操作を介して状態を1に設定します。
保護された最終的なブールアンドセットステート(int expect、int update){//このreturn unsafe.compareandswapint(this、stateoffset、expect、update)をサポートするための内因性セットアップについては以下を参照してください。 }現時点では、NextCは0ではなく、Falseを返します。 CountDown()メソッドが2回呼び出されるまで、State == 0、nextc == 0まで待ちます。この時点でtrueを返します。
DoreLeaseShared()メソッドを入力します。
doreLeaseShared(); ↓Private void doreleaseshared(){ / * *他の *進行中の取得 /リリースがある場合でも、リリースが伝播することを確認します。これは、 *信号が必要な場合、ヘッドのパーククセッサーを非難しようとする通常の *方法で進行します。ただし、そうでない場合は、ステータスが伝播するように設定されています *リリース時に伝播が続くことを確認します。 *さらに、これを行っている間に新しいノードが追加された場合に備えて、ループする必要があります。また、 * unparksuccesserの他の使用とは異なり、CASがステータス *をリセットするかどうかを知る必要があります。 */ for(;;){node h = head; if(h!= null && h!= tail){int ws = h.waitstatus; if(ws == node.signal){if(!compareandsetwaitstatus(h、node.signal、0))継続; //ケースを再確認するループUnparksuccessor(h); } else if(ws == 0 &&!compareandsetwaitstatus(h、0、node.propagate))継続; //故障したcasのループ} if(h == head)// head headが破損した場合はループ。 }}現時点で待機中のキューモデルを思い出してください。
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
この時点で、頭はヌルや尾ではありません。 waitstatus == node.signal、したがって、(!compareandsetwaitstatus(h、node.signal、0))if(!compareandsetwaitstatus)を入力します。
if(!compareandsetwaitstatus(h、node.signal、0))↓ /*** cas waitstatus field of a node。 */private static final boolean compareandsetwaitstatus(node node、int expect、int update){return unsafe.compareandswapint(node、waitstatusoffset、expect、update);}このCAS操作は状態を0に設定します。つまり、頭の中のwaitstatusはこの時点で0です。キューモデルは次のとおりです
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
この方法はtrueを返します。 parksuccessor(h)を入力します。
Unparksuccessor(h); ↓Private void unparksuccesser(ノードノード){ / * *ステータスが負の場合(つまり、信号が必要である可能性がある)、シグナルを見越してクリアしてみてください。これが失敗した場合、または待機スレッドによってステータスが変更された場合は問題ありません。 */ int ws = node.waitstatus; if(ws <0)compareandsetwaitstatus(node、ws、0); / * *スレッドへのスレッドは後継者に保持されます。これは通常 *次のノードです。しかし、キャンセルされた場合、または明らかにnullの場合は、 *尾から後方に移動して、実際の *非キャンセルの後継者を見つけます。 */ node s = node.next; if(s == null || s.waitstatus> 0){s = null; for(node t = tail; t!= null && t!= node; t = t.prev)if(t.waitstatus <= 0)s = t; } if(s!= null)locksupport.unpark(s.thread);}Sは、ヘッドの後継ノード、つまり現在のスレッドのノードです。 s!= null、およびs.waitstatus == 0なので、locksupport.unpark(s.thread);
public static void upark(thread thread){if(thread!= null)unsafe.unpark(thread); }つまり、ロック解除されたスレッドがブロックされます。審判はwhiを吹くことを許可されました!
CountDown()の原則は非常に明確です。
CountDown()メソッドが実行されるたびに、状態は1。状態== 0まで、キューでブロックされたスレッドがリリースされ始め、その後のノードのスレッドは前身ノードのWaitStatusの状態に従ってリリースされます。
わかりました、前の記事の質問に戻りましょう。次のループが発生するのはいつですか(待ち合わせ方法のループ)
for(;;){final node p = node.pred deverseor(); if(p == head){int r = tryacquireshared(arg); if(r> = 0){setheadandpropagate(node、r); p.next = null; // gc failed = false;戻る; }} if(shuldparkafterfailedacquire(p、node)&& parkandcheckinterprupt())throw new interruptedexception();}この時点で、state == 0なので、setheadandpropagateメソッドを入力してください。
setheadandpropagate(node、r); ↓private void setheadandpropagate(ノードノード、int propagate){node h = head; // sethead(ノード)の下のチェックのために古い頭を記録します。 / * *次のキュームノードの信号を試みます: *伝播が発信者によって示された *または以前の操作によって録音された( * h.waitStatusは * waitstatusとして)チェックは *不必要な目覚めを引き起こす可能性がありますが、複数のレースが獲得/リリースがある場合にのみ、ほとんどの場合、とにかく信号が必要です *。 */ if(propagate> 0 || h == null || h.waitstatus <0 ||(h = head)== null || h.waitstatus <0){node s = node.next; if(s == null || s.isshared())doreLeaseshared(); }}↓private void sethead(node node){head = node; node.thread = null; node.prev = null;}この方法は、頭の後継ノードを頭に変更します。この方法の後、ノードの次のノードがnullに設定され、モデルが次の図になります
前後 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
つまり、ノードヘッドテールと他のものはNULLに設定されており、GCがリサイクルするのを待っています。この時点で、戻って、forループから飛び出し、キューがクリアされます。
ここにプロセス全体のデモがあります
setheadandpropagate(node、r); +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------スレッド= null | <----ノード(テール)| currentThread | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ノード(テール)| currentThread | + --------------------------------+↓ +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CountDownLatchのコアはブロッキングスレッドキューです。これは、スレッドとWaitStatusを含むリンクリストから構築されたキューで、WaitStatusが後継ノードスレッドステータスを説明しています。
状態は非常に重要な旗です。構築するとき、それは対応するn値に設定されます。 n!= 0の場合、スレッドが中断されない限り、ブロッキングキューは常にブロックされます。
CountDown()メソッドが呼び出されるたびに、State-1が使用され、await()メソッドを使用して、State == 0までブロッキングキューにメソッドを呼び出すスレッドを追加し、スレッドをリリースできません。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。