同時-CountDownLatchの実現原理を深く分析する
8768 ワード
一、前言
最近
二、本文
2.1抽象キュー同期器AQS
次の内容を読む前に、必ずAQSの実現原理を勉強してください.
2.2 CountDownLatchの実現原理
すでに
2.3 CountDownLatchの内部クラス
前述したように、
内部クラスSyncの実装は非常に簡単であり、
2.4 CountDownLatchのメンバー変数と構築方法
次に、
2.5 awaitメソッド分析
ここを見て、
2.6 countDownメソッド分析
次に、
理解を容易にするために、
三、まとめ
四、参考 JDK1.8ソース
最近
java.util.concurrent
パッケージのいくつかの常用クラスを研究して、前にAQS
、ReentrantLock
、ArrayBlockingQueue
およびLinkedBlockingQueue
の関連ブログを書いて、今日このブログは書いて発注したもう一つの常用クラス--CountDownLatch
を書きます.ここでまず、CountDownLatch
はAQS
に基づいて実現され、AQS
こそスレッド同期を実現したコンポーネントであり、CountDownLatch
はその使用者にすぎないので、CountDownLatchを学ぶには、AQSの実現原理を理解しておく必要があります.私の以下の説明は、AQS
を理解した上で確立されています.私は前にAQS
実現原理の分析ブログを書いたことがあります.興味があれば、同時--抽象キュー同期器AQSの実現原理を見てみましょう.二、本文
2.1抽象キュー同期器AQS
CountDownLatch
を言う前に、AQS
を先に言わなければなりません.AQS
全称抽象キュー同期器(AbstractQuenedSynchronizer)は、スレッド同期を実現するための基礎フレームワークである.もちろん、それは私たちが理解しているSpring
というフレームワークではありません.それはクラスです.クラス名はAbstractQuenedSynchronizer
です.スレッドの同期を完了できるロックや類似の同期コンポーネントを実現したい場合は、AQS
を使用して実現することができます.それはスレッドの同じステップをカプセル化しているので、私たちは自分のクラスで使用しています.私たち自身のロックを簡単に実現することができます.AQS
の実現は比較的複雑で、短いいくつかの言葉ではっきり言えない.私は以前、AQS
の実現原理を分析するブログを書いたことがある.同時--抽象キュー同期器AQSの実現原理.次の内容を読む前に、必ずAQSの実現原理を勉強してください.
CountDownLatch
の実現は非常に簡単で、完全にAQS
に依存しているので、私の以下の説明はすべて理解したAQS
の基礎の上に構築されています.上の推薦ブログを読むこともできますし、自分で関連資料を調べることもできます.2.2 CountDownLatchの実現原理
すでに
CountDownLatch
の実現原理を学び始めた以上、その役割はきっとすでに知っているに違いない.私はここで詳しく展示しない.簡単に紹介する:CountDownLatch
のドア栓と呼ばれ、ドアの鍵と見なすことができ、ドアに多くの鍵を与え、すべての鍵が解けてこそ、通過することができる.スレッドの場合、CountDownLatch
はスレッドの実行をブロックし、CountDownLatc
の内部レコードの値が0
に減少した場合にのみ、スレッドは前進し続けることができます.CountDownLatch
下層はAQS
で実現され、AQS
の一般的な使用方法は内部クラスの形で継承され、CountDownLatch
はこのように使用される.CountDownLatch
の内部にはSync
から継承された内部クラスAQS
があり、AQS
のロック解除方法を再書き込み、Sync
のオブジェクトを介してAQS
の方法を呼び出し、スレッドの実行をブロックする.CountDownLatch
オブジェクトを作成する場合、count
メソッドを通過するには、count
が0
に減少した場合にのみ、await
メソッドを通過する必要があります.そうしないと、await
にブロックされます.ここでは実際には、スレッドがawaitメソッドに実行されると、ロックを取得する必要があり(ロックはAQSによって実現される)、countが0でないと、スレッドはロックを取得できず、ブロックされる.countが0であれば、スムーズに通過できます.CountDownLatch
は使い捨てであり、count
の値を増やす方法がないため、すなわち、count
が0
に減少すると、その後も0
になり、スレッドをブロックすることはできなくなる.次に、ソースコードの観点からCountDownLatch
を分析します.2.3 CountDownLatchの内部クラス
前述したように、
CountDownLatch
内部にはSync
という内部クラスが定義されており、AQS
から継承されており、この内部クラスによってスレッドブロックが実現されています.次に、この内部クラスの実装を見てみましょう.private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
/** , count , count 0 , await */
Sync(int count) {
// CountDownLatch AQS count AQS state
// state count
setState(count);
}
/** count */
int getCount() {
return getState();
}
/**
* AQS acquireShared、acquireSharedInterruptibly ,
* , , AQS ,
* 0, , ,
* , count 0,
*/
protected int tryAcquireShared(int acquires) {
// state 0, count 0,
// count 0, 1, , ,
// count 0, -1, ,
// CountDownLatch
return (getState() == 0) ? 1 : -1;
}
/**
* AQS , true ,
* AQS releaseShared ,
* CountDownLatch , count
*/
protected boolean tryReleaseShared(int releases) {
//
for (;;) {
// state , count
int c = getState();
// count 0, , false
// false, 0 ,
// true,AQS , false, ,
// , false
if (c == 0)
return false;
// count 0, -1
int nextc = c-1;
// compareAndSetState count c, nextc
// CAS ,
if (compareAndSetState(c, nextc))
// nextc == 0, true, , ,
// nextc > 0, , false
return nextc == 0;
}
}
}
内部クラスSyncの実装は非常に簡単であり、
AQS
で提供される共有ロックを使用するインタフェースであるtryAcquireSharedとtryReleaseSharedの2つの方法しか実装されていないことがわかります.これは、AQS
が実際には共有ロックメカニズムであることを示している.すなわち、ロックが同時に複数のスレッドによって取得されることができる.これは、CountDownLatch
が0に減少すると、すべてのスレッドがcount
メソッドを通過すると、ロックが取得されないためにブロックされないため、スムーズに通過することができるからである.また、上記の実装から、await
は、Sync
の値をcount
のAQS
の値として直接使用し、state
の値が0である限り、スレッドはロック、すなわち実行権限を取得することができる.2.4 CountDownLatchのメンバー変数と構築方法
次に、
state
の属性と構造方法を見てみましょう./**
* , Sync , AQS ,
*/
private final Sync sync;
/**
* , count
*/
public CountDownLatch(int count) {
// count 0
if (count < 0) throw new IllegalArgumentException("count < 0");
// Sync , count ,Sync setState(count)
this.sync = new Sync(count);
}
2.5 awaitメソッド分析
CountDownLatch
類の最も核心的な2つの方法はCountDownLatch
とawait
であり、まずountDown
方法の実現を見てみましょう.// , count 0
public void await() throws InterruptedException {
// sync acquireSharedInterruptibly , AQS
// , , AQS
// 。 , , ,
// 。
sync.acquireSharedInterruptibly(1);
}
await
の実現は異常に簡単で、わずか1行のコードしかなく、await
でカプセル化された方法を呼び出した.これがAQS
の利点であり、AQS
はスレッドのブロックと起動メカニズムを実現し、実現の複雑さを隠すが、他のクラスは簡単に使用するだけでよい.わかりやすいように、AQS
方法を見てみましょう./** AQS , , */
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// , ,
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared , Sycn ,
// count == 0, 1, , ,
// count < 0, doAcquireSharedInterruptibly ,
// Sync tryAcquireShared
if (tryAcquireShared(arg) < 0)
// , , AQS ,
// , count == 0, ,
// , , ,
// , count == 0 ,
doAcquireSharedInterruptibly(arg);
}
ここを見て、
acquireSharedInterruptibly
の実現原理について比較的明確な理解が得られたと信じています.CountDownLatch
の実現は完全にCountDownLatch
に依存しており、以上の内容が理解できない場合は、先にAQS
を勉強してください.2.6 countDownメソッド分析
次に、
AQS
のもう一つのコアの方法を分析します.CountDownLatch
/**
* count -1, count 0 ,
*/
public void countDown() {
// sync releaseShared , AQS , AQS ,
// , , false, , false,
// , AQS ,
// CountDownLatch , count - 1, count 0,
// ,
sync.releaseShared(1);
}
理解を容易にするために、
countDown
中AQS
方法の実現を見てみましょう.public final boolean releaseShared(int arg) {
// tryReleaseShared , Sycn ,
// tryReleaseShared true, count , 0 , doReleaseShared
if (tryReleaseShared(arg)) {
// AQS ,
// acquireSharedInterruptibly ,
// , count == 0,
// , , count == 0 ,
doReleaseShared();
return true;
}
return false;
}
三、まとめ
releaseShared
のソースコードを直接見てみると、その実現は本当に簡単で、注釈を含めて、合計CountDownLatch
行コードで、注釈を除いて、300
行コードさえありません.これは、100
を書き換える2つの方法を除いて、基本的にはAQS
が提供するテンプレートメソッドを呼び出すことである.したがって、AQS
を理解する過程は、実際にはCountDownLatch
を理解する過程であり、AQS
を理解し、AQS
の原理を理解すれば、CountDownLatch
分を必要としない.5
は本当にAQS
の合併の中で非常に重要なコンポーネントであり、多くの種類はそれに基づいて実現されている.例えば、Java
があり、同時にReentrantLock
も面接の常考点であるため、よく研究しなければならない.最後に、私が前に書いたAQS
に関するソース分析ブログ:同時-抽象キュー同期器AQSの実現原理を再びお勧めします.四、参考