blog

再入可能ロック解除処理のソースコード詳細

unparkingプロセスは、パークが解除されるプロセスであるため、理解する必要があります。前のドキュメントでは、ロック...

Oct 22, 2020 · 4 min. read
シェア
  • なぜt2は内容のないHeadを初期化するのですか?
  • なぜt3はt2のwaitStatus=-1を変更するのですか?
  • 再入場ロックの解除方法は?
  • ノードがスレッドを保存する意味は?

質問しながらソースコードを読む

t1unlockを例にとってみましょう。

アンロック

 public void unlock() {
 sync.release(1);
 }

リリース

 public final boolean release(int arg) {
 //1.tryRelease ロックの解放を試みる
 if (tryRelease(arg)) {
 Node h = head;
 //2.ロックが正常に解除された後、2つの判定がある
 //3.head = null 待ち行列がなければheadはnull、なければ待ち行列がある 
 //4.h.waitStatus  =0 waitStatus  =0 それは-1。
 //ロックのプロセスを理解する必要がある。例えば、t2はheadのwaitStatusを-1そしてpark
 //  h.waitStatus = -1 説明 hのポイントが変更されたとは、hのポイントがパークにあり、パークを解除する必要があることを意味する。unpark
 if (h != null && h.waitStatus != 0)
 //5.条件が満たされた後、次のノードのパークを解除するためにunparkSuccessorに行く。 
 unparkSuccessor(h);
 return true;
 }
 return false;
 }

h != null && h.waitStatus != 0 要約すると 、releaseは3つのステップに分ける必要があります 1. tryReleaseでロックの解放を試みます 2. ロックの解放に成功した後、待機中のスレッドをウェイクアップする必要があるかどうかを判断します 3. unparkSuccessorでスレッドのパークを解除します

トライリリース

 protected final boolean tryRelease(int releases) {
 // state-1 
 int c = getState() - releases;
 //現在のスレッドは、ロックが解除される前にロックを保持しているスレッドと等しい。 
 if (Thread.currentThread() != getExclusiveOwnerThread())
 throw new IllegalMonitorStateException();
 boolean free = false;
 //ロック再入力時に、状態が0に等しいかどうか、またどのような場合に0に等しくならないかを判断する。 
 //リエントラント・ロックでなければ、ロックを解除してロックを保持している現在のスレッドを設定すればいい。=null
 if (c == 0) {
 free = true;
 setExclusiveOwnerThread(null);
 }
 //リエントラントロックで、状態をリセットするとロックが1つ減る。
 setState(c);
 return free;
 }

要約:tryRelease 1.casが状態を変更することによってロックを解放 2.それが再入可能なロックである場合は、単に状態を変更 3.再入可能なロックでない場合は、現在のロックの保持スレッド= NULLを変更 4.ロックが正常に解放され、trueを返しますそうでなければfalseを返します

アンパークサクセサー

 private void unparkSuccessor(Node node) {
//  node = head
//なぜなら、t2は前のノードであるヘッドのwaitStatusを-1
 int ws = node.waitStatus;
//0より小さい場合は -1 
 if (ws < 0)
 //casでノード、つまりヘッドを修正する。 WaitStatus = 0 最初に戻す
 compareAndSetWaitStatus(node, ws, 0);
 //次のノードを取得する
 Node s = node.next;
 //unLock判定は終わったが...。 s.waitStatus = -1 それが来る前に。
 //なぜ s.waitStatus>0,スレッドt2が割り込まれた場合、t2のwaitStatusがunprakより大きくなる可能性があるからだ。0
 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)
 //スレッドをウェイクアップするためにuparkメソッドを呼び出し、そのスレッドにnode.threadt2に到達するスレッド
 LockSupport.unpark(s.thread);
 }

要約:unparkSuccessor 1は、スレッドが中断されるのを防ぐために、-1に等しいwaitStatusは、ノードをウェイクアップする必要性の下で判定を繰り返すことです 2は、スレッドをウェイクアップするunparkメソッドを呼び出す 3は、ここまでそれがt1であっても、ロックを解除し、t2をウェイクアップ

スレッド起動後の実行開始位置

駐車しているところから解錠するので、ロックを追加するプロセスを理解する必要があります。前の文書は、ロック

起動されたスレッドはacquireQueuedから実行を開始します。

 final boolean acquireQueued(final Node node, int arg) {
 boolean failed = true;
 try {
 boolean interrupted = false;
 for (;;) {
 //unparkその後、ここまでのデッドループを実行する
 //t2の最後のノードを取る。head
 final Node p = node.predecessor();
 //ノードがヘッド・ノードかどうかを判断し、ヘッド・ノードであればロックしようとし、ロック・ロジックを通過する。 tryAcquire
 //ここではt2がロック解除に成功している。 
 if (p == head && tryAcquire(arg)) {
 //t2をヘッド・ノードとしてセットし thread = nullスレッドを保持する必要がないからだ。
 setHead(node);
 //ヘッドの次をnull 
 p.next = null; // help GC
 failed = false;
 return interrupted;
 }
 //このパークにロックした後、unparkもここから実行される。
 if (shouldParkAfterFailedAcquire(p, node) &&
 parkAndCheckInterrupt())
 interrupted = true;
 }
 } finally {
 if (failed)
 cancelAcquire(node);
 }
 }

概要:acquireQueued 1.unparkによってウェイクアップされた後、スレッドはacquireQueuedから実行を開始します 2.まず、ウェイクアップされたノードの最後のノードがヘッドノードであるかどうかを判断します 3.2を満たしている場合、tryAcquireを呼び出してロックの取得を試みます 4.ウェイクアップされたノードがロックを取得した後、ヘッドノードを現在のノードに設定します 5.ロック取得後、通常のビジネスロジックの実行

これでアンロックはクリアです。

ソート

  • ロックを解除するたびに、ロックを解除する必要があるかどうかを判断するために、t2はヘッドを仮想化する必要があります。
  • なぜt3がt2のwaitStatus=-1を変更する必要があるかというと、ロックを解除するときに、head.waitStatusが-1かどうかを判断します。
  • リエントラント・ロックはどのように解除されますか?リエントラント・ロックは、casをstate-1
  • ノードストア・スレッドの使い道は何ですか?unprakを呼び出すときに、パークを解除する必要があるスレッドを渡すことができるのですか?
Read next

イーサネット技術はどのようにセキュリティを提供するか

ビーコンチェーンを理解するには、まずイーサ2.0の中核機能であるシャーディング技術を理解してください。チェーンはビーコンチェーンとは別のもので、フェーズ1でのみ導入が予定されている機能です。しかし、まずこの機能を理解することで、Ether 2.0の全体的な技術アーキテクチャとその設計内容を理解することができます。イーサネット1.0では、システムによって処理される1秒あたりのトランザクション数が多く...

Oct 22, 2020 · 2 min read