Java並行プログラミングの要約9:詳細な条件

詳細については、以下を参照してください。条件の待機およびシグナル待機/通知メカニズムの詳細な説明

全体として、オブジェクトの待機および通知/通知は、オブジェクトモニターと連携して、スレッド間の待機/通知メカニズムを完了します。

そして、ConditionとLockが協力して、待機中の通知メカニズムを完成させます。

前者はJavaの最下位レベルにあり、後者は言語レベルにあり、より高い制御性とスケーラビリティを備えています。

2つの使用方法の違いに加えて、機能特性にはまだ多くの違いがあります

  1. 条件は無応答割り込みをサポートできますが、オブジェクトモードを使用することはできません。
  2. 条件は複数の待機キュー(新しい複数の条件オブジェクト)をサポートできますが、オブジェクトモードは1つしかサポートできません。
  3. 条件はタイムアウト時間の設定をサポートできますが、オブジェクトはサポートしません。

 

1.条件実装の原則

1.待機キューの実現原理

条件オブジェクトの構築が渡されlock.newCondition(),、このメソッドは実際にAQSの内部クラスであるConditionObjectオブジェクトを作成します。

ロックメカニズムの実装では、AQSは同期キューを内部的に維持します。排他ロックの場合、ロックの取得に失敗したすべてのスレッドのテールが同期キューに挿入されます。同様に、条件は内部でも使用されます。同じ方法で、1つは内部で維持されます。 キューを待機すると、condition.awaitメソッドを呼び出すすべてのスレッドが待機キューに追加され、スレッドの状態が待機状態に変換されます。

ConditionObjectには、firstWaiterlastWaiterの2つのメンバー変数があります

/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

ConditionObjectは、待機キューのヘッドポインタとテールポインタを保持することにより、待機キューを管理していることがわかります。

NodeクラスはAQSのNodeクラスを再利用し、そのノードステータスと関連する属性はAQSの実装で確認できます。

Nodeクラスには次のような属性があります。

//後続ノード
NodenextWaiter;

待機キューは一方向キューであり、前にAQSと言ったとき、同期キュー双方向キューであることがわかりました

  • condition.awaitメソッドを呼び出した後、スレッドは順番に待機キューに挿入されます。図に示すように、キュー内のスレッド参照はThread-0、Thread-1、Thread-2 ... Thread-8です。
  • 待機キューは一方向キューです。

lock.newCondition()メソッドを複数回呼び出して、複数の条件オブジェクト作成できます。つまり、ロックは複数の待機キューを保持できます

オブジェクトを使用する以前の方法は、実際には、オブジェクトオブジェクトモニターに1つの同期キューと1つの待機キューしか存在できないことを意味します。

並行パッケージのロックには、同期キューと複数の待機キューがあります

2.待機の原則:

現在のスレッドがcondition.await()メソッドを呼び出した後、現在のスレッドはロックを解放し、それがsignal / signalAllになるまで待機キューに追加します。これにより、現在のスレッドは待機キューから同期に移動します。ロックが取得されるまでキューに入れます。その後、awaitメソッドから戻るか、待機中に中断されると中断されます。

2.1。現在のスレッドを待機キューに追加するにはどうすればよいですか?

現在のスレッドによってカプセル化されたノードは、テール挿入によって待機キューに挿入できます。同時に、待機キューは先行ノードのないチェーンキューであることがわかります。AQSを学習する前に、同期がキューは先頭ノードでした。チェーンキュー、これは2つの違いです。

2.2。ロックを解除するプロセスは?

AQSテンプレートメソッドreleaseメソッドを呼び出して、AQSの同期状態を解放し、同期キュー内のヘッドノードの後続ノードによって参照されるスレッドをウェイクアップします。解放が成功した場合は正常に戻り、失敗した場合は例外がスローされます。

2.3。awaitメソッドを終了するにはどうすればよいですか?

現在のスレッドが中断されるか、condition.signal / condition.signalAllメソッドが呼び出されます。現在のノードが同期キューに移動した後 、これは現在のスレッドがawaitメソッドを終了するための前提条件です。

whileループを終了した後、acquireQueued(node, savedState)実装の基礎となるAQSの導入について述べたときにこのメソッドを呼び出します。興味があれば、この記事参照してください。このメソッドの役割は、同期ステータスを取得するための継続的な試行のスレッドスピニングプロセスにあります。成功するまで(スレッドはロックを取得します)これは、awaitメソッドの出口が、条件参照(関連付け)を取得したロックでなければならないことも示しています

3.signalとsignalAllの実現原理:

signalまたはsignalAllの条件メソッドを呼び出すと、待機キューで待機時間が最も長いノードを同期キュー移動できるため、ノードはロックを取得できます。待機キューは先入れ先出し(FIFO)であるため、待機キューのヘッドノードは待機時間が最も長いノードである必要があります。つまり、条件のシグナルメソッドが呼び出されるたびに、ヘッドノードがヘッドノードになります。同期キューに移動されます。

条件シグナルを呼び出すための前提条件は、現在のスレッドがロックを取得していることです。このメソッドは、待機キュー内のヘッドノード、つまり待機時間が最も長いノードを作成し、同期キューに移動して、待機中のスレッドを起動する前の同期キューがウェイクアップされました。つまり、awaitメソッドを呼び出したスレッドを正常に終了させるためにawaitメソッドのLockSupport.park(this)メソッドから戻りました。

signalAllとsignalメソッドの違いは、doSignalAllメソッドに反映されます。doSignalメソッドは待機キューのヘッドノードでのみ動作することはすでにわかっています。このメソッドは、待機キュー内のすべてのノードが同期キューに移動されることのみを認識します。つまり、現在condition.await()メソッドを呼び出している各スレッドに「通知」します。

4.2つの考え方を組み合わせる:

待機通知メカニズムは、条件によって提供されるawaitメソッドとsignal / signalAllメソッドを使用して実現できます。このメカニズムは、最も古典的な問題である「生産者と消費者の問題」を解決できます。

この絵はとても重要です。この絵を理解して覚えていると言えます!

 

おすすめ

転載: blog.csdn.net/ScorpC/article/details/113859719