一般的な
複数のスレッドがリソースを奪い合うときAQSフレームデータ構造は双方向のFIFOキューで、失敗した競争これらのスレッドは、キューに追加されます。彼は、上部層、取得共有モードインターフェースacquireSharedされたいずれかのインターフェイスの多くを提供します。本稿では、このインタフェースの段階の分析により、ステップに基づいて行われます、リソースへのアクセスがキューにスレッドをどのように失敗している、キューがリソースを再び競争する方法にキューは、次のようにacquireSharedが行われ、一般的な流れです。
-
リソースを共有するGetメソッドを呼び出すことにより、複数のスレッドをTryAcquireShared、戻り値が0以上取得されたリソース成功に等しい、戻り値が0未満の獲得失敗です。
-
現在のスレッドの故障後に、このスレッドパッケージノードノードのaddWaiterメソッドを呼び出すことにより、共有リソースを取得し、ノードが共有モードに設定されています。ノードは、キューの末尾に追加されます。
-
エンドに添加した後、ノードのノード上で決定される新しいヘッドとしてノードセットにsetHeadAndPropagateメソッドを呼び出している間、ヘッドノードは、そのノードは、共有資源ノードキューと取得された場合、キューノードの先頭ではありませんノード、ノードのすべての共有キュータイプは、共有リソースを取りに行く、目を覚まします。買収に失敗した場合は、再度キューに追加。
-
現在のノード前駆体ノードは、ヘッドノード、エンドスピンするまで待つ自転用貫通ノードが第1ノード前駆ノードでない場合、ループ。
これは、リソースの共有モードAQSの競合は、一般的なプロセスを失敗している、ここで私たちは一般的な印象を持たせ、ソースコードの以下の詳細な分析を介して動作する方法です。
AQS共有ロックモード
AQSはこのトップレベルのacquireShared()メソッドを呼び出すことにより、共有ロックを取得し、我々は、このメソッドのソースコードを見て:
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared()戻り値が0で、ロックを取得するdoAcquireSharedを入力し()メソッドが失敗した未満である場合、場合、この方法は、決意を有しています。tryAcquireSharedメソッドは値を返す()このメソッドは、彼に私達の表情の焦点であるtryAcquireSharedため、共有モードでロックを取得するために使用されます。これはjdk1.8で書かれています
*失敗の場合は負の値@return。ゼロ共有で買収場合
*モードは成功したが、後続の共有モードの取得が可能
*成功しません。正の値の取得で共有場合
*モードは成功し、その後の共有モードが威力を取得し
、その後の待機中のスレッドが、その場合には、*も成功
*可用性をチェックしなければなりません。(3つの異なるのサポート
*戻り値はコンテキストで使用されるように、この方法を可能に
したら取得だけ時々排他的に行動するところ。*)
*成功、このオブジェクトが取得されています。
復帰の失敗は、それが成功のために共有モード0で取得された場合は、負のですが、次のノードの共有モードと、それは成功しません。リターンは、現在のスレッドは、共有の成功モデルを取得し、その背後のスレッドは、共有モードを取得することができる示し0より大きい正数である場合。
共有モードの取得に失敗したとき、私たちは運転をdoAcquireShared何するためにソースコードを見て
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
主ノードノードとしてパッケージされ、ノードはキューの末尾に追加される最初の呼び出しaddWaiter()メソッド。ここで、着信共有モデルパラメータは、ノードが共有モードとなります。
現在のスレッドは、キューに追加され、その後、スピン前駆により取得されたノード(のための(;;))、先行ノードは、ヘッドノードは、コールtryAcquireShared()メソッドは、このメソッドが戻り値を上回るを有することが、現在のノードのステータスを得るためである場合導入された、0が背後にあるすべてのノードを起動しますだけ大きいよりも、後続ノードを覚ますしていない0の手段に等しいです。
正常に共有リソースを取得した場合、コールsetHeadAndPropagate方法は、現在のノードが最初のノードで設定し、キューノードの元の頭をしましょう。スピンロックを取得する処理で中断した場合、現在のスレッドが中断されました。
スレッドがブロックされている場合、前駆体ノードはshouldParkAfterFailedAcquireによって、現在のノードの先頭でない場合は、スレッドの現在の状態を決定するtrueを返し、それ以外の場合はfalseを返す。現在のスレッド状態がブロック場合ParkAndCheckInterrupt方法は、割り込みイベントかどうかのロックを取得する過程で、現在のスレッドを指しそして、それはフラグを入れて、その後中断された真に割り込まアップデートです。
例外がコールcancelAcquireメソッドを発生した場合、このメソッドは、現在のノードの状態が解除されている更新プログラム、およびノードをクリアすることです。
我々は、このメソッドのソースコードを見てsetHeadAndPropagate
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);//设置当前节点为头节点
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {//符合状态的将全部唤醒
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
二つのパラメータ、現在のノードの送達のこの方法は、戻り値はtryAcquireShared方法です。我々は、現在のノードがヘッドノードsetHeadをロックするように設定され得るために、現在のヘッドノードは、それ()メソッド最初のレコードからソースコードを参照してください。文は、ノードが、次の呼び出しがdoReleaseSharedメソッド空の場合、ウェイクアップ後続ノードの要件を満たすために継続した場合、doReleaseShared方法は、ウェイクノードの後ろを続けています。共有ロックが解放されるには、この方法を詳細に説明します。
共有ロックが解除されます
私たちは、このメソッドは、リソースを解放するモードのトップメソッドを共有している、ソースコードを見てReleaseShared。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//
doReleaseShared();
return true;
}
return false;
}
首尾よく放出された場合、共有リソースモデルの放出を得るための方法をtryReleaseShared、そしてdoReleaseSharedは、次のノードをウェイクアップし続けて呼び出します。
私たちは、特定のウェイクアップ動作doReleaseShared()このメソッドを見ていき
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
現在のスレッド状態がNode.SIGNALある場合、我々は、ソースコードを発見した、Node.SIGNAL値は、-1される静的定数であり、この値は、現在のスレッドは中断されていることを示します。現在のスレッドが中断された場合、更新は継続失敗した場合、スレッドの現在の状態が0である更新されます。成功した更新後のコールunparkSuccessor()このメソッドは、最初のノードのウェイク共有ロックです。ヘッドノード自身がリセット状態waitStatus == 0に属し、そしてそれは、その後ダウン伝播ノードの伝播状態に設定されている場合。
このメソッドのソースコードを見てみましょうunparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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);
}
この方法から、我々は、スレッドの状態の頭部がゼロ未満であれば、それは0未満であるなぜ、そして置く現在のスレッドが0にリセットされていることが判明し、記事は、waitStatus <0待ちや一時停止状態を述べています。これは、現在のスレッドが保留状態を待っている場合、現在のスレッドの状態が0にリセットされ、です。次のノードがキャンセルされた空またはスレッドの下にある場合、次のノードを見つけ、その後、ケースノードを見つけるためにヘッドから除去されていないがあります。現在のスレッドのウェイクアップコールLockSupport.unpark方法の次のノードが空でない場合。LockSupport.unparkは、このクラスは、ネイティブメソッドの実行を呼び出す危険を呼び出します。