前に書く
AQSを学習するためのCountDownLatchソースコード
デモ
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(()->{
try {
System.out.println("线程1执行");
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
new Thread(()->{
try {
System.out.println("线程2执行");
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
countDownLatch.await();
System.out.println("两个线程都执行完执行此段代码");
}
- メインスレッド、スレッド1、スレッド2が並列実行を開始
- メインスレッドがcountDownLatch.await()待機に遭遇します
- 2秒後、スレッド1の実行が完了し、countDownLatch.countDown()が実行されます。
- 5秒後、スレッド2の実行が完了し、countDownLatch.countDown()が実行されます。
- 2回のカウントダウンの後、メインスレッドはブロックが通過するのを待ちます
- 回数は工法の価値によって異なります
ソースコード分析
工法
状態値を2に設定し、他のロジックはありません
countDownLatch.await()
最終的なコール
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//尝试获取锁
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
- 状態値を取得します。0に等しい場合は1を返し、0に等しくない場合は-1を返します。
- 状態が0に等しい場合、ダウンを続けることができることがわかっています。例としてのデモによると、初期状態値は2であり、0に等しくありません(スレッド1とスレッド2はcountDownを実行しませんでした)。 -1を返します
続けてdoAcquireSharedInterruptibly
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
既視感はありますか?これがaqsの方法です。この方法の論理については前に説明しました。つまり、メインスレッドがclhキューに追加され、共有状態に設定されてから、それがヘッドの次のノードであるかどうかを引き続き判別し、共有ロックの取得を試みます。正常に返されます。失敗し、現在のスレッドをブロックします
- tryAcquireSharedは前のものとは異なります。これはcountdownlatchのサブクラス実装です。状態が0かどうかを判断するだけで済みます。状態が0の場合のみ、スレッドはダウンし続けます。
- ここでメインスレッドキューがブロックされていることを覚えています
- キューメンバーヘッド->メインスレッドここでは1つのメインスレッドのみがブロックされます
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
countDownLatch.countDown()
最後の呼び出し、またはaqsのメソッド
public final boolean releaseShared(int arg) {
//尝试释放锁
if (tryReleaseShared(arg)) {
//释放成功后唤醒线程
doReleaseShared();
return true;
}
return false;
}
CountDownLatchサブクラスのTryReleaseShared実装
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
- 無限ループとcasは、マルチスレッドの問題を考慮に入れることです
- 最初に状態を取得します。デモの例によると、状態は2です。
- 状態が0の場合、ロックは解放されているため、再度解放する必要はありません。
- casを使用して2を1に変更します。マルチスレッド実行の場合、失敗したスレッドは次のループを実行し、1を0に設定します。
- 最後に、状態が0に設定されているスレッドはtrueを返します
doReleaseShared
または、共有ロックをウェイクアップするaqsメソッドについては、セマフォのソースコード分析を参照してください。ウェイクアップ後、メインスレッドはブロックされて解放されます。次のループが実行された後、状態がすでに0の場合、ビジネスコードが実行されます。
上記はCountDownLatchの一般的なソースコード分析です。間違いがあれば、批判や訂正を歓迎します。