- なぜ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を呼び出すときに、パークを解除する必要があるスレッドを渡すことができるのですか?