前の記事の分析を通じて、排他的モードでロックを取得する3つの方法、つまり応答スレッド割り込みなしで取得し、応答スレッド割り込みを取得し、タイムアウト時間を取得する3つの方法があることを知っています。共有モードでロックを取得するこれらの3つの方法もあり、基本的に同じです。 1つの方法を理解すれば、他の方法をすばやく理解することができます。 Abstractueuedsynchronizerのソースコードには1000行以上がありますが、何度も繰り返されているため、読者は最初は怖がらないはずです。辛抱強くゆっくりと読むだけで、自然に徐々に理解できます。私の個人的な経験では、Abstractqueuedsynchronizerソースコードを読む際に理解するためのいくつかの重要な側面、つまり排他モードと共有モード、ノードの待機状態、条件付きキューの理解の違いがあります。これらの重要なポイントを理解すれば、その後のソースコードの読み取り値がはるかに簡単になります。もちろん、これらは私の記事「Java Concurrencyシリーズ[1] ---- AbstractQueuedSynchronizerソースコード分析」で紹介されており、読者は最初にそれをチェックすることができます。この記事では、共有モードを分析して、ロックを取得する3つの方法とロックを解放する1つの方法を分析します。
1.スレッド割り込みの取得に応答しない
//インターラッダブルモードでロックを取得する(共有モード)パブリックファイナルボイドAcquireshared(int arg){// 1。ロックを取得してみてください(tryacquireshared(arg)<0){// 2。買収が失敗した場合は、この方法を入力してください。 }} //ロックを取得しようとします(共有モード)//負の数:獲得が失敗したことを示します//ゼロ値:現在のノードが正常に取得されたことを示しますが、後継ノードはもはや正の数値を取得できますが、現在のノードは正常に取得され、後継ノードが継承されたプロトインテストのトライアックインテストである(Int Aguiresheard) unsupportedoperationexception();}Acquiresharedメソッドを呼び出すことは、スレッド割り込みに応答せずにロックを取得する方法です。この方法では、TryAcquiresharedが最初に呼び出され、ロックを取得しようとします。 TryAcquiresharedメソッドは、ロックを取得する状態を返します。ここで、AQSは、戻りステータスが負の場合、現在のノードがロックを取得できないことを意味することを指定します。 0が現在のノードがロックを取得することを意味するが、後続のノードはもう取得できない場合。それが正である場合、それは現在のノードがロックを取得することを意味し、このロックの後続のノードも正常に取得できます。サブクラスがTryAcquiresharedメソッドでロックを取得する論理を実装する場合、返品値はこの慣習を遵守する必要があります。 Tryacquiresharedを呼び出すことの返品値が0未満の場合、ロックを取得する試みが失敗したことを意味します。次に、Doacquiresharedメソッドを呼び出して、現在のスレッドを同期キューに追加します。 Doacquiresharedメソッドが表示されます。
//同期キューで(共有モード)を取得しますプライベートボイドdoacquireshared(int arg){//同期キュー最終ノードnode = addwaiter(node.shared); boolean failed = true; try {boolean挿入= false; for(;;){//現在のノード最終ノードp = node.pred decustror()のフォワードノードを取得します。 //フォワードノードがヘッドノードの場合、(p == head){//再度ロックを取得して取得ステータスを返してみてください// r <0を返してください。正常に取得されるint r = tryacquireshared(arg); (r> = 0){//この目的のために、現在のノードがロックを正常に取得したことを示します。この時点で、ロックステータス情報を後続のノードSetheadandPropagate(Node、R)に伝播します。 p.next = null; //スレッドブロッキング中に割り込み要求が受信された場合、(中断された){selfinterrupt(); }失敗= false;戻る; }} //ロックの取得が失敗するたびに、スレッドを一時停止できるかどうかが判断されます。可能であれば、スレッドはparkandcheckinterruptメソッドで吊り下げられます(parkafterfailedacquire(p、node)&& parkandcheckinterprupt()){interruped = true; }}} finally {if(failed){cancelacquire(node); }}}最初にdoacquiresharedメソッドを入力して、Addwaiterメソッドを呼び出して、現在のスレッドをノードに包み、同期キューの最後に置きます。排他的なモードについて話すときにノードを追加するプロセスについて話しましたので、ここでは話しません。ノードが同期キューに入ると、その前のノードがヘッドノードであることがわかった場合、ヘッドノードのスレッドがロックを取得して部屋に入ったため、ロックを取得する順番です。したがって、現在のノードは最初に自分自身を掛けることはありませんが、再びロックを取得しようとします。前の人がロックを解放して去る場合、現在のノードはロックを正常に取得できます。前の人がロックをリリースしていない場合、ShouldFailedacquireメソッドを呼び出します。この方法では、ヘッドノードの状態が信号に変更されます。前のノードの状態が信号であることを保証することによってのみ、現在のノードは自信を持って固執することができます。すべてのスレッドは、parkandcheckinterruptメソッドで吊り下げられます。現在のノードがたまたまロックを正常に取得した場合、SetheadandPropagateメソッドが呼び出され、ヘッドノードとして設定し、後ろに共有モードのノードを起動します。 SetheadandPropagateメソッドの特定の操作を見てみましょう。
//ヘッドノードを設定し、ロックの状態(共有モード)の状態を伝播します。 //指定されたノードをヘッドノードsethead(ノード)として設定します。 //プロパゲートが0より大きい場合、ロックがif(伝播> 0 || null || h.waitstatus <0){//指定されたノードnode s = node.next; next;の後継ノードを取得します。 //指定されたノードの後継ノードが空である場合、またはその状態が共有状態である場合(s == null || s.isshared()){//後継ノードを覚醒させるdoreleaseshared(); }}} //ロック操作をリリースします(共有モード)プライベートボイドdoreleaseshared(){for(;;){//同期キューノードh = headのヘッドノードを取得します。 if(h!= null && h!= tail){//ヘッドノードint ws = h.waitstatusの待機状態を取得します。 //ヘッドノードのステータスが信号の場合、誰かがif(ws == node.signal){//ヘッドノードの待機状態を0(!compareandsetwaitstatus(h、node.signal、0)){continue; } //後継ノードUnparksuccesser(h)を覚ます; //ヘッドノードのステータスが0の場合、誰も後でキューイングしていないことを意味します。ヘッド状態を変更して伝播するだけです} else(ws == 0 &&!compareandsetwaitstatus(h、0、node.propagate)){continue; }} //ヘッドノードが期間中に変更されていないことを確認することによってのみ、(h == head){break; }}}SetheadandPropagateメソッドを呼び出すと、最初にヘッドノードとして自分自身を設定し、渡されたTryAcquiresharedメソッドの返品値に基づいて後継ノードを目覚めるかどうかを決定します。前述のように、戻り値が0より大きい場合、現在のノードがロックを正常に取得し、後続のノードもロックを正常に取得できることを意味します。この時点で、現在のノードは、共有モードでもあるノードを覚ます必要があります。目を覚ますたびに、次のノードを目覚めるだけであることに注意してください。後者のノードが共有モードにない場合、現在のノードは部屋に直接入り、さらにノードを起動しません。共有モードで後継ノードを目覚めさせる操作は、Doreleasesharedメソッドで実行されます。共有モードと排他的モードのウェイクアップ操作は基本的に同じです。どちらもあなたの座席(待機状態)でブランドを見つけます。ブランドがシグナルである場合、それは誰かが後でそれを起こすのを手伝う必要があることを意味します。ブランドが0の場合、これは誰もこの時点でキューに並んでいないことを意味します。排他的なモードでは、誰もキューイングしていないことがわかった場合は、キューを直接残します。共有モードでは、誰もキューの後ろにキューインしていないことがわかった場合、現在のノードはまだ小さなメモを残してから、後の人々にこのロックの利用可能な状態を伝えるために残して(待機状態を伝える)。その後、後で来る人がこの状態に基づいてロックを直接取得するかどうかを判断できるとき。
2。スレッド割り込み取得への応答
//中断モードでロックを取得する(共有モード)パブリックファイナルvoid accoquireshared -interdrictibly(int arg)arturnedexception {//最初にスレッドが中断されるかどうかを判断します。 } // 1。ロックを取得してみてください(tryacquireshared(arg)<0){// 2。買収が失敗した場合は、この方法を入力します。 }} //割り込みモードで取得(共有モード)プライベートボイドdoacquireshared途中で(int arg)arturnedexceptionをスローする{// synchronization queue final node node node = addwaiter(node.shared)のテールに現在のノードを挿入します。 boolean failed = true; try {for(;;){// if(p == head){int r = tryacquireshared(arg); if(r> = 0){setheadandpropagate(node、r); p.next = null;失敗= false;戻る; }} if(shuldparkafterfailedacquire(p、node)&& parkandcheckinterprupt()){//ブロッキングプロセス中にスレッドが割り込み要求を受信した場合、ここですぐに例外がスローされます。 }}} finally {if(failed){cancelacquire(node); }}}スレッド割り込みに応じてロックを取得する方法と、スレッド割り込みに応じてロックを取得する方法は、基本的にプロセスで同じです。唯一の違いは、スレッド割り込み要求に応答する場所です。スレッド割り込みがスレッド割り込みに応答してロックを取得していない場合、スレッドはParkandCheckinterruptメソッドから目覚めます。目覚め後、割り込み要求が受信されたかどうかはすぐに返されます。割り込み要求が受信されたとしても、割り込みリクエストに応答して自らを掛けるまで、取得されるまで回転し続けます。スレッドは、スレッドが目覚めた後、割り込み要求にすぐに応答します。ブロッキングプロセス中にスレッド割り込みが受信された場合、中断拡張がすぐにスローされます。
3.取得するタイムアウト時間を設定します
//限られたタイムアウト(共有モード)でロックを取得するパブリックファイナルブールトリックアッキーリシャレッドナノス(int、long nanostimeout)を投げます{if(thread.interrupted()){throw new interruptedException(); } // 1。 TryAcquiresharedを呼び出してロックを取得しようとします// 2。買収が失敗した場合、Doacquiresharednanosを呼び出してくださいtryacquireshared(arg)> = 0 || doacquiresharednanos(arg、nanostimeout);} //限られたタイムアウト(共有モード)でロックを取得するPrivate Boolean Doacquiresharednanos(int、long nanostimeout)は、中断されたexceptionをスローします{long last = system.nanotime();最終ノードnode = addwaiter(node.shared); boolean failed = true; try {for(;;){//現在のノード最終ノードp = node.pred decustrer()の前のノードを取得します; if(p == head){int r = tryacquireshared(arg); if(r> = 0){setheadandpropagate(node、r); p.next = null;失敗= false; trueを返します。 }} //タイムアウトが使用されている場合、取得は終了し、障害情報が返されます(nanostimeout <= 0){return false; } // 1。スレッドサスペンション要件が満たされているかどうかを確認します(フォワードノードステータスが信号であることを保証)// 2。タイムアウト時間がスピン時間よりも大きいかどうかを確認します(ParkafterFailedAcquire(P、ノード)&& Nanostimeout> SpinfortimeoutThreshold){//上記の2つの条件が満たされている場合、現在のスレッドはlocksupport.parknanos(これ、ナノスティムアウト); } long now = system.nanotime(); //タイムアウト時間ロック取得の時間を差し引くたびにnanostimeout- = now -lasttime;最後の= now; //ブロッキング中に割り込み要求が受信された場合、例外がすぐにスローされます(thread.interrupted()){new interruptedexception(); }}} finally {if(failed){cancelacquire(node); }}}上記の2つの取得方法を理解している場合、タイムアウト時間の取得方法を簡単に設定できます。主にタイムアウトメカニズムを理解するために、基本的なプロセスは同じです。ロックが初めて取得された場合、Doacquiresharednanosメソッドが呼び出され、タイムアウト時間が渡されます。メソッドを入力した後、状況に応じてロックが再び取得されます。ロックが再び障害が発生した場合、スレッドは吊り下げられていると見なされる必要があります。この時点で、タイムアウト時間がスピン時間よりも大きいかどうかを判断します。その場合、スレッドは一定期間吊り下げられます。そうでなければ、私たちはそれを取得しようとし続けます。ロックを取得するたびに、ロックの時間を差し引いて取得します。タイムアウト時間が使い果たされるまで、このようにループします。ロックが取得されていない場合、取得が終了し、取得障害フラグが返されます。スレッドは、期間を通してスレッド割り込みに応答します。
4.共有モードでのノードの操作を削除します
//リリースロックの操作(共有モード)パブリックファイナルブールリリースリリース(int arg){//1.try lock if(tryreleaseshared(arg)){// 2。リリースが成功した場合は、他のスレッドを覚ます(); trueを返します。 } return false;} //ロック(共有モード)保護されたboolean tryreleaseshared(int arg){throw new unsupportedoperationexception();} //リリースロックの操作(共有モード)プライベートボイドdoreleaseShared(){/;(;;;;; if(h!= null && h!= tail){//ヘッドノードint ws = h.waitstatusの待機状態を取得します。 //ヘッドノードのステータスが信号の場合、誰かが後でキューイングすることを意味します(ws == node.signal){//ヘッドノードの待機状態を0 if(!compareandsetwaitstatus(h、node.signal、0)){continue; } //後続のノードUnparksuccesser(h)を覚ます; //ヘッドノードのステータスが0の場合、それは後で誰もキューイングしていないことを意味します。ヘッド状態を伝播するために変更するだけです} else(ws == 0 &&!compareandsetwaitstatus(h、0、node.propagate)){continue; }} //ループは(h == head){break; }}}スレッドが部屋で作業を終了した後、ロックを解放するためにリリースシェアードメソッドを呼び出します。まず、ロックをリリースしようとするTryreleaseSharedメソッドを呼び出します。このメソッドの判断ロジックは、サブクラスによって実装されます。リリースが成功した場合は、DoreleaseSharedメソッドに電話して後継ノードを覚醒させます。部屋から出た後、元の座席(ヘッドノード)を見つけて、誰かがシートに小さなメモを残したかどうかを確認します(ステータス信号)。もしそうなら、後継ノードを起こしてください。 (ステータス0)がない場合、誰もキューにキューインしていないことを意味します。去る前に最後にしなければならないことは、去る前に去ることです。つまり、その座席に小さなメモを残すことです(ステータスは伝播するように設定されています)。ロックリリースプロセス全体と排他的モードの唯一の違いは、この最後のステップで動作することです。
注:上記の分析はすべてJDK1.7に基づいており、異なるバージョン間に違いがあります。読者は注意を払う必要があります。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。