AQSの内部原則は何ですか?

AQS内部主成分分析

AQSの内部原理を分析するには、要点を把握する必要があります。AQSの内部は比較的複雑で、コードが非常に長く、非常に読みにくいためです。ジャンプしてソースコードを読むと、完全に把握してください。そのため、2回目では、AQSの3つのコア部分をキーポイントとして抽出し、これら3つの部分をAQSの扉を開くためのエントリポイントとして使用しました。

3つの主要な部分は何ですか?AQSの3つのコア部分は、状態、キュー、取得/解放などの重要なメソッドであり、コラボレーションツールによって実装されることが期待されていますこれらの3つの部分から始めて、別々に分析します。

状態状態

最初に説明するのは状態状態です。AQSがコラボレーションツールの基本的なフレームワークとして管理または機能する場合は、何らかの状態を管理する必要があり、この状態はAQS内の状態変数で表されます。これは次のように定義されます。

/**
 * The synchronization state.
 */
private volatile int state;

状態の意味は静的ではなく、特定の実装クラスのさまざまな役割に応じてさまざまな意味表します。ここにいくつかの例を示します。

たとえば、セマフォでは、stateは残りのライセンスの数を表します。最初に状態を10に設定すると、最初は10個のライセンスがあり、スレッドがライセンスを取得すると、状態は9になるため、セマフォの状態は内部カウンターと同等になります。

別の例として、CountDownLatchツールクラスでは、stateは「カウントダウン」する必要のある数を表します当初は5に設定されていると想定していました。毎回CountDownメソッドを呼び出すと状態が1減少し、0に減少するとラッチが解除されます。

ReentrantLockの状態の意味を見てみましょう。ReentrantLockでは、ロックの占有率を表します初期値は0で、ロックを保持しているスレッドがないことを意味します。状態が1になると、ロックがスレッドによって保持されていることを意味します。

では、なぜ2、3、4になるのでしょうか。なぜそれが合計されるのですか?ReentrantLockは再入可能であるため、同じスレッドが再びロックを所有できることを再入可能と呼びます。同じスレッドによってロックが複数回取得されると、状態は徐々に増加し、stateの値は再入可能者の数を表します。解放すると徐々に減少します。たとえば、最初は4の場合、解放後は3になり、解放後は2になります。このように実行される縮小操作は、2または1に縮小しても実行されます。ロックを表していないロックを保持しているスレッドはありません。0に減少した場合にのみ、この時点で元の状態に復元されます。つまり、現在ロックを保持しているスレッドはありません。したがって、状態が0の場合は、ロックがどのスレッドによっても占有されていないことを意味します。つまり、ロックは現在解放された状態にあり、他のスレッドはこの時点でロックの取得を試みることができます。

これは、さまざまなクラスにおけるさまざまな状態の意味の具体的な表現です。3つの例を引用しました。新しいツールで将来AQSを使用する場合は、状態を使用してビジネスロジックを表し、このクラスに必要な状態も使用する必要があります。

状態変更の問題をもう一度見てみましょう。状態は複数のスレッドで共有され、同時に変更されるため、状態を変更するすべてのメソッドは、状態がスレッドセーフであることを確認する必要があります。ただし、状態自体はvolatileによってのみ変更され、volatile自体はスレッドの安全性を確保するのに十分ではないため、状態を変更するときに同時実行の安全性を確保するためにAQSがどのような設計を使用するかを見てみましょう。

状態に関連する2つのメソッド、compareAndSetStateとsetStateを引用します。これらの実装は、AQSによって完了しています。つまり、これら2つのメソッドを直接呼び出して、状態にスレッドセーフな変更を加えることができます。これら2つのメソッドのソースコードがどのように実装されているかを見てみましょう。

  • まず、私たちがよく知っているCAS操作であるcompareAndSetStateメソッドを見てみましょう。このメソッドのコードは次のとおりです。
protected final boolean compareAndSetState(int expect, int update) {
    
    
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

このメソッドには、return unsafe.compareAndSwapInt(this、stateOffset、expect、update)という1行のコードしかありません。このメソッドは、すでによく知られています。UnsafeでCAS操作を使用し、CPU命令の原子性を使用します。この操作の原子性を確保するため。これは、以前に導入されたスレッドセーフを確保するためのアトミッククラスの原則と一致しています。

  • 次に、以下に示すように、setStateメソッドのソースコードを確認します。
protected final void setState(int newState) {
    
    
    state = newState;
}

状態値を変更し、state = newStateを直接割り当てることで、値が直接割り当てられることが非常に簡単であることがわかります。混乱するかもしれません。同時の安全処理、ロック、CASはありません。どうすればスレッドセーフを確保できますか?

ここでは、volatileの役割について説明します。前述のvolatileキーワードについては、2つのシナリオに適用できることを知っていました。シナリオの1つは、基本タイプの変数が直接割り当てられている場合、volatileが追加されている場合です。スレッドセーフを確保できます。これは、volatileの非常に一般的な使用シナリオであることに注意してください。

/**
 * The synchronization state.
 */
private volatile int state;

状態は基本型であるint型であり、ここでのsetStateメソッドはstateに直接割り当てられていることがわかります。前の値の読み取りや元の値の変更は含まれないため、使用するのはこの場合、volatileは同時実行の安全性を保証できます。そのため、setStateメソッドはスレッドセーフです。

状態の概要は次のとおりです。AQSには状態などの属性があり、揮発性によって変更され、同時に変更されます。これは、現在のツールクラスの特定の状態を表し、クラスごとに異なる意味を持ちます。

FIFOキュー

AQSの2番目のコア部分であるFIFOキュー、つまり先入れ先出しキューを見てみましょう。このキューの主な機能は、待機中のスレッドを格納することです。多くのスレッドが同時にロックを取得したいとすると、ほとんどのスレッドはロックを取得できません。では、ロックを取得できないスレッドをどのように処理するのでしょうか。それらを保存および管理するには、キューが必要です。したがって、AQSの主な機能はスレッドの「キュー・マネージャー」として機能することです。

複数のスレッドが同じロックをめぐって競合する場合、キューイングメカニズムを使用して、ロックを取得できなかったスレッドをつなぎ合わせる必要があります。現在のスレッドがロックを解放した後、マネージャーは適切なスレッドを選択します。解放されたばかりのロック。そのため、AQSはこのキューを維持し、待機中のすべてのスレッドをキューに入れています。

このキューは二重リンクリストの形式であり、そのデータ構造は単純に見えますが、多くのマルチスレッド同時実行の問題を考慮する必要があるため、スレッドセーフな双方向キューを維持することは非常に複雑です。AQSの作者であるDougLeaによって提供されたこのキューの図を見てみましょう:(
ここに画像の説明を挿入します
この画像は英語のドキュメントの画像から引用されています

キューでは、ヘッドノードとテールノードはそれぞれヘッドノードとテールノードを表すために使用され、どちらも初期化時に空のノードを指します。ヘッドノードは「現在ロックを保持しているスレッド」と理解でき、ヘッドノードの後のスレッドはブロックされ、ウェイクアップされるのを待ちます。AQSはウェイクアップも担当します。

取得/解放方法

AQSの3番目のコア部分である取得/リリース方法を見てみましょう。AQSには、上記の状態とキューに加えて、非常に重要な別の部分、つまり、取得と解放の重要なメソッドがあります。これらのメソッドはコラボレーションツールクラスのロジックの具体的な表現です。各コラボレーションツールクラスはそれ自体で実行する必要があります。実装。したがって、ツールクラスが異なれば、実装と意味も異なります。

入手方法

まず、取得方法を見てみましょう。フェッチ操作は通常、状態変数の値に依存します。状態値に応じて、コラボレーションツールクラスのロジックも異なり、フェッチ時にブロックされることがよくあります。以下のいくつかの具体例を見てみましょう。

たとえば、ReentrantLockのlockメソッドは「取得メソッド」の1つです。実行中に、状態が0に等しくなく、現在のスレッドがロックを保持しているスレッドではないことが判明した場合、ロックが他のスレッドによって保持されています。もちろん、この時点ではロックを取得できないため、スレッドはブロッキング状態に入ることができます。

別の例として、セマフォの取得方法は、ライセンスを取得するために使用される「取得方法」の1つです。このとき、ライセンスを取得できるかどうかは、状態の値にも依存します。状態値が正の数の場合はライセンスが残っていることを意味し、十分な数であれば正常に取得できますが、状態が0の場合は無料ライセンスがないことを意味します。スレッドはそれを取得できません。ライセンスはブロッキング状態になるため、ここでも状態の値に関連しています。

別の例として、CountDownLatch取得メソッドはawaitメソッド(オーバーロードされたメソッドを含む)であり、「カウントダウンが終了するまで待機する」ために使用されます。待機を実行すると、状態の値が判断されます。状態が0に等しくない場合、他のスレッドがカウントダウンメソッドを実行して状態を0に減らすまで、スレッドはブロック状態になります。これは、ラッチが解放されたことを意味します。そのため、以前にブロックされていたスレッドが目覚めます。

「取得メソッド」はクラスごとに意味が異なりますが、状態値に関連していることが多く、スレッドがブロッキング状態になることがよくあります。これは、AQSにおける状態状態の重要な位置を証明するものでもあります。クラス。

リリース方法

リリース方法は取得方法の反対であり、通常は現在取得方法と組み合わせて使用​​されます。先ほど説明した取得方法でスレッドがブロックされる場合があります。たとえば、ロックが取得されていない場合、スレッドはブロック状態になりますが、解放方法では通常、スレッドはブロックされません

たとえば、セマフォセマフォでは、releaseはreleaseメソッド(オーバーロードされたメソッドを含む)であり、release()メソッドの関数はライセンスを解放することです。これにより状態が1増加します。CountDownLatchでは、releaseはcountDownメソッドです。 、関数はカウントダウンです。数値、状態から1を引いたものとします。したがって、実装クラスが異なれば、状態に対する操作も完全に異なり、各コラボレーションクラスは独自のロジックに従って実装する必要があることもわかります。


さらに読むAQSのコア構造を分析するために、さらに読みましょう。AQSの内部構造を理解することは非常に有益ですが、AQSの全体像を含めるだけでは十分ではありません。AQSをさらに理解することに興味がある場合は、関連する拡張リソースを学ぶことを選択できます。

総括する

AQSの3つの最も重要な部分。1つ目は状態です。これは値であり、クラスごとに異なる意味を意味し、多くの場合状態を表します。2つ目はスレッドを格納するために使用されるキューです。3つ目は「取得/解放」です。AQSの関連メソッドAQSのツールクラスを使用して、独自のロジックに従って実装する必要があります。

おすすめ

転載: blog.csdn.net/Rinvay_Cui/article/details/114017718