CountDownLatchソースコード分析-Wait()、特定のコンテンツは次のとおりです
前の記事では、CountDownLatchの使用方法について説明しました。この記事では、ソースコードレベルからのawait()の原則について説明します。
Latchカウントがゼロ(またはスレッドの中断)になるまで、待機が現在のスレッドをブロッキング状態に保つことができることをすでに知っています。
以下はそのソースコードです。
end.await(); ↓public void await()throws interruptedexception {sync.acquireshared intrupdibly(1);}同期は、CountDownLatchの内部クラスです。これがその定義です。
プライベート静的最終クラスの同期は、abstractqueuedsynchronizer {...}を拡張しますAbstractqueuedsynchronizerを継承します。 Abstractueuedsynchronizerこのクラスは、Javaスレッドの非常に重要なクラスに属します。
FIFO待機キューに依存するブロッキングロックと関連する同期(シグナル、イベントなど)を実装するフレームワークを提供します。
続けて、Abstractqueuedsynchronizerクラスにジャンプします。
sync.acquireshared interctibly(1); ↓public final void acchireshared -interrumdily(int arg)// abstractqueuedsynchronizer interruptedexception {if(thread.interrupted())throw new interruptedException(); if(tryacquireshared(arg)<0)doacquireshared interctibly(arg);}ここには2つの判断があります。最初に、スレッドが中断されているかどうかを判断し、次に次の判断を下します。ここでは、主に2番目の判断を見ます。
保護されたint tryacquireshared(int quickires){return(getState()== 0)? 1:-1;}TryAcquiresharedメソッドが同期して実装されていることに注意する必要があります。
Abstractqueuedsynchronizerには実装がありますが、デフォルトの実装は例外をスローすることです。
TryAcquiresharedこのメソッドは、現在のオブジェクトのステータスがロックの取得を許可できるかどうかを照会するために使用されます。
同期して、状態が0であるかどうかを判断することにより、対応するINT値を返すことがわかります。
では、状態とはどういう意味ですか?
/***同期状態。 */民間揮発性INT状態;
上記のコードは、状態が同期ステータスを表すことを明確に示しています。
状態は揮発性キーワードを使用してそれを変更することに注意する必要があります。
揮発性キーワードは、状態の変更がすぐにメインメモリに更新されるようにすることができます。他のスレッドを読む必要がある場合、新しい値はメモリで読み取られます。
つまり、状態の可視性が保証されています。最新のデータです。
ここに来る状態は何ですか?
ここでは、CountDownLatchのコンストラクターを見る必要があります。
CountDownLatch end = new CountDownLatch(2); ↓public countdownlatch(int count){if(count <0)Throw new IllegalargumentException( "count <0"); this.sync = new sync(count);}↓sync(int count){setState(count);}コンストラクターの数値は状態を設定するために使用されることがわかります。
したがって、ここにstate == 2があります。 tryacquiresharedは-1を返します。以下に入力してください
doacquireshared intruptibly(arg); ↓private void doacquireshared -interdrictibly(int arg)throws arturnedexception {final node node = addwaiter(node.shared); boolean failed = true; try {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())new interruptedexception(); }}最後に{if(failed)cancelacquire(node); }}OK、このコードは少し長く、いくつかの関数が呼び出されます。それを一つずつ見てみましょう。
新しいクラスノードが最初の行に表示されます。
ノードは、チェーン構造を定義するAQS(AbstractQueuedSynchronizer)クラスの内部クラスです。以下に示すように。
+------+prev+-----++-----+head | | <---- | | <---- | | |テール +----- + +----- + +----- +
この構造を覚えておいてください。
コードaddwaiter(node.shared)の最初の行にもメソッドがあります。
addwaiter(node.shared)//node.sharedとは、ノードが共有モードであることを意味します↓プライベートノードaddwaiter(node mode){node node = new node(thread.currentthread()、mode); // ENQの高速パスを試してください。障害ノードpred = tailで完全なENQにバックアップします。 //プライベート一時的な揮発性ノードテール。 if(pred!= null){node.prev = pred; if(compareandsettail(pred、node)){pred.next = node;ノードを返す; }} enq(node);ノードを返す;}まず、ノードが構築され、現在のスレッドが保存されます。モードは共有モードです。
テールとは、待機中のキューのキューエンドが現時点でヌルであることを意味します。したがって、pred == nullはENQ(ノード)に入ります。
ENQ(ノード)↓プライベートノードENQ(最終ノードノード){for(;;){node t = tail; if(t == null){// if(compareandsethead(new node()))tail = headを初期化する必要があります。 } else {node.prev = t; if(compareandsettail(t、node)){t.next = node; tを返します。 }}}}同じ尾がnullです。
Compareandsethead(new node())↓/*** CASヘッドフィールド。 ENQのみが使用します。 */private final boolean compareandsethead(ノードアップデート){unsafe.compareandswapobject(this、headoffset、null、update);}これはCAS操作です。ヘッドがnullの場合、待機中のキューのヘッドが更新値に設定されます。これは新しいノードです。
テール=ヘッド;その後、この時点で尾はもうヌルではなくなりました。次のサイクルを入力します。
今回は、最初にノードの前のポインターをテールに向けてから、CAS操作を介してテールにノードを設定し、キューのテール、つまりノードを返します。
待機キューのモデルは次のように変更されます
+ ------+ prev +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OK、ここに到達すると、待機方法が戻ります。これは、現在のスレッドのノードに等しいスレッドです。
Doacquireshared途中で(int arg)に戻り、次のループを入力します。
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();}この時点で、状態がまだ0より大きいと仮定すると、この時点でr <0であるため、SuldParkafterFailedAcquireメソッドを入力してください。
SuldParkafterFailedAcquire(P、ノード)↓プライベート静的ブール値は、parkafterfailedacquire(node pred、node node){int ws = pred.waitstatus; if(ws == node.signal)// static final int signal = -1; / * *このノードは、リリースを要求するステータスをすでに設定している *ため、安全に駐車できます。 */ return true; if(ws> 0){ / * *前身がキャンセルされました。前任者をスキップし、 *再試行を示します。 */ do {node.prev = pred = pred.prev; } while(pred.waitstatus> 0); pred.next = node; } else { / * * waitstatusは0または伝播する必要があります。信号が必要であるが、まだ駐車しないでください。発信者は、駐車前に取得できないことを確認するために *再試行する必要があります。 */ compareandsetwaitstatus(pred、ws、node.signal); } return false;}↓/*** cas waitstatus field of a node。 */private static final boolean compareandsetwaitstatus(node node、int expect、int update){return unsafe.compareandswapint(node、waitstatusoffset、expect、update);}あなたは、ParkafterFailedacquireが比較するためにずっと進んでいることを見ることができます。
compareandsetwaitstatusは、node.signalにprevのwaitstatusを設定します。
node.signalとは、後続のノードのスレッドが格差を持たない必要があることを意味します(目覚めたものと同様)。この方法はfalseを返します。
このサイクルの後、キューモデルが次の状態になります
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
parkafterfailedacquireがfalseを返す必要があるため、以下の条件を検討しなくなります。 (;;)にループを続けます。
状態がまだ0を超えている場合は、再度入力して、ParkafterFailedAcquireが必要です。
今回は、頭のwaitstatusがnode.signalであるため、parkafterfailedacquireはtrueを返します。
今回は、parkandcheckinterruptメソッドを見る必要があります。
プライベートファイナルブールParkandCheckEnterrupt(){locksupport.park(this); return thread.interrupded(); }OK、スレッドが中断されていないので、falseを返します。 (;;)にループを続けます。
状態が常に0より大きく、スレッドが中断されない場合、それは常にこのループにあります。つまり、審判は前の記事で、ゲームの終わりを発表することに常に渋っていると述べました。
それでは、どのような状況でループが発生しますか?つまり、どのような状況で0が0未満になりますか?次の記事について説明します。
要約すると、await()メソッドは、実際にキューを初期化し、待機する必要があるスレッド(状態> 0)をキューに追加し、WaitStatusを使用して後継ノードのスレッドステータスをマークすることです。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。