AQSは、共有ロックモードを説明します

一般的な

 

複数のスレッドがリソースを奪い合うときAQSフレームデータ構造は双方向のFIFOキューで、失敗した競争これらのスレッドは、キューに追加されます。彼は、上部層、取得共有モードインターフェースacquireSharedされたいずれかのインターフェイスの多くを提供します。本稿では、このインタフェースの段階の分析により、ステップに基づいて行われます、リソースへのアクセスがキューにスレッドをどのように失敗している、キューがリソースを再び競争する方法にキューは、次のようにacquireSharedが行われ、一般的な流れです。

  1. リソースを共有するGetメソッドを呼び出すことにより、複数のスレッドをTryAcquireShared、戻り値が0以上取得されたリソース成功に等しい、戻り値が0未満の獲得失敗です。

  2. 現在のスレッドの故障後に、このスレッドパッケージノードノードのaddWaiterメソッドを呼び出すことにより、共有リソースを取得し、ノードが共有モードに設定されています。ノードは、キューの末尾に追加されます。

  3. エンドに添加した後、ノードのノード上で決定される新しいヘッドとしてノードセットにsetHeadAndPropagateメソッドを呼び出している間、ヘッドノードは、そのノードは、共有資源ノードキューと取得された場合、キューノードの先頭ではありませんノード、ノードのすべての共有キュータイプは、共有リソースを取りに行く、目を覚まします。買収に失敗した場合は、再度キューに追加。

  4. 現在のノード前駆体ノードは、ヘッドノード、エンドスピンするまで待つ自転用貫通ノードが第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は、このクラスは、ネイティブメソッドの実行を呼び出す危険を呼び出します。

おすすめ

転載: www.cnblogs.com/fengyun2050/p/12384608.html