マルチスレッド - 同時のたCountDownLatchツールは、詳細な

簡単な紹介

多くの場合、スレッドは、すべての子の後に終了した要約する場面を待つために並列タスクで実行するメインスレッド複数のスレッド、およびメインスレッドのニーズにあなたの毎日の開発に有効にする必要がありますが発生しました。

たCountDownLatchの一般的な用途の1つは柔軟性は十分ではありませんスレッドがこれを達成するために()メソッドに参加する前に表示されますが、この方法に参加JDKがたCountDownLatchこのクラスを提供しますので、さまざまなシーンのニーズを満たすことができません。

たCountDownLatch援助が同期され、実行の他のスレッドが完了するまでスレッドは、より多くの操作のグループを待つか、することができます

たCountDownLatch二つの主な方法は、一件の以上のスレッドがのawaitメソッドを呼び出してありますが、これらのスレッドがブロックされています。カウントダウン他のスレッドの呼び出しは、方法は継続、のawaitメソッドブロックのスレッドがウェイクアップされますので、カウンタの値が、ゼロになった場合、1で対抗します。

注:これは、リセット、カウントすることができない1回限りのツールです

あなたがカウントをリセットすることができるようにする必要がある場合は、我々はCyclicBarrierを使用します。

シナリオ

  1. すべての子スレッドの待ちは、メインスレッドを終了してからダウン実行されます。
  2. 同時に実行し、ラッチカウントが0になった後まで待つすべてのスレッド。

次の例では、たCountDownLatchの使用は、すべてのスレッドの実装後にメインスレッドを同時に開始されたすべての子スレッド、すべての子供のためのメインスレッドの待ち時間を達成し、その後ダウン実行です。

public class CountDownLatchDemo {
	public static void main(String[] args) throws InterruptedException {
		CountDownLatch startLatch = new CountDownLatch(1);
		CountDownLatch downLatch = new CountDownLatch(5);

		for (int i = 0; i < 5; i++) {
			new Thread(new worker(startLatch,downLatch),"线程:"+i).start();
		}
		TimeUnit.SECONDS.sleep(1); //休眠1秒,保证所有线程已经调用了await方法
		System.out.println("所有任务都已启动");
		startLatch.countDown();
		System.out.println("等待所有任务完成");
		//主线程阻塞等待,直到所有子线程完成
		downLatch.await();
		System.out.println("所有任务完成");
	}
}

class worker implements Runnable{

	private CountDownLatch startLatch;
	private CountDownLatch downLatch;

	public worker(CountDownLatch startLatch, CountDownLatch downLatch) {
		this.startLatch = startLatch;
		this.downLatch = downLatch;
	}
	@Override
	public void run(){
		System.out.println(Thread.currentThread().getName()+"准备工作完成");
		try {
			startLatch.await(); //等待所有工人准备完毕后再开始工作
		} catch (Exception e) {
			e.printStackTrace();
		}
		work();
		try {
			System.out.println(Thread.currentThread().getName()+"工作完成");
			downLatch.countDown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void work() {
		System.out.println(Thread.currentThread().getName()+"开始工作");
		try {
			TimeUnit.SECONDS.sleep(RandomUtil.randomInt(5));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

結果を印刷します:

印刷結果

たCountDownLatchの違いや参加方法

1つの違いは()メソッドの後に参加する子スレッドを呼び出し、子スレッドが完成し、実行中になるまでスレッドがブロックされたままになる、とされたCountDownLatchは、子スレッドが終了ランニングを持っているか、されたCountDownLatch缶での操作にカウントダウンできるようにカウンタを使用することです任意の時間は必ずしもなくても、そののawaitメソッドが返すように、実行中の子スレッドは、スレッドが終了するまで待機します。

また、通常、スレッドプールにスレッドRunableを管理するために、スレッドプールを使用して直接加え、この時間は方法がないスレッドを呼び出す方法を再結合し、それはたCountDownLatchは、スレッド同期方法は、より柔軟な制御を可能とするために私たちに参加するために比較されます。

原則たCountDownLatch

その上たCountDownLatch名からは、その内部カウンタを推測する必要がありますし、カウンタがデクリメントされます。

private final Sync sync;

private static final class Sync extends AbstractQueuedSynchronizer {
    //省略
}

たCountDownLatch内部使用Syncは、同期もAbstractQueuedSynchronizerを継承し、そう、達成するために、この内部の静的クラスである底部はAQSを使用して達成されます

一般的な方法:

コンストラクタ

次のコンストラクタによって、それが見つけることができ、実際には、カウンタの値は、AQSの状態変数の初期化状態に割り当てられている、本明細書にAQS値カウンタ値の状態を表すために使用されます

注:たCountDownLatchの初期化は、カウントを与えられなければならない場合には、スレッドの数を定義するために使用される数がブロックREADY。

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

Sync(int count) {
    setState(count);
}

空のawait()メソッド

スレッドの呼び出しは、メソッドたCountDownLatchオブジェクトを待つ場合は、次のいずれかが返されますが起こるまで、現在のスレッドはブロックされます。

  • 場合、すべてのスレッドは、カウンタ値が0である場合、である、方法カウントダウンたCountDownLatchオブジェクトを呼び出します。
  • 他のスレッドの呼び出しは、現在のスレッドの割り込み、現在のスレッド()メソッドを中断し、現在のスレッドが例外:InterruptedException例外をスローしてから戻ります。

内部メソッドは、AQSは(待つ呼び出すことですかで次の一見)は次のとおりです。

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

あなたは上記のコードから見ることができる、のawait()メソッドは、AQSのacquireSharedInterruptiblyメソッド同期デリゲートを呼び出します。

次のように後者のコードは:

//AQS获取共享资源时可被中断的方法
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    //如果线程被中断则抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //查看当前计数器值是否为0,
    //若为0,tryAcquireShared方法返回1,不符合if条件,则直接返回;
    //若不为0,则进入AQS的队列等待
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

//sync类实现的AQS的接口
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

プロセスのコードの特徴から見たリソースへの中断スレッドアクセスすること、及びリソースは、取得した共有リソースであることができます。

acquireSharedInterruptibly()メソッドは、最初の例外がスローされた場合、現在のスレッドが、中断されたかどうかを決定する、または呼がtryAcquireShared()現在の状態値(カウンタ値)を表示するための方法を達成する同期、現在のスレッドがのawait()メソッドの戻りで、0でありますそうでない場合は、現在のスレッドがブロックさせることAQS doAcquireSharedInterruptibly()メソッドを呼び出します。さらに引数パラメータが使用されていないtryAcquireShared渡された場合、本方法は、現在の状態値をチェックするtryAcquireShared順序が0でない呼び出され、そのためCAS現在の状態値マイナス1と呼ばれていない。、ことがわかります

ブールのawait(長いタイムアウト、TimeUnitでユニット)方法

スレッドは、このメソッドたCountDownLatchオブジェクトを呼び出すと、次のいずれかが返されますが起こるまで、現在のスレッドはブロックされます。

  • すべてのスレッドがメソッドカウントダウンたCountDownLatchオブジェクトを呼び出すと、カウンタ値が0であるとき、あること、この時間は、trueを返します。
  • タイムアウトリターン偽のためのタイムアウト時間を設定します。
  • 他のスレッドの呼び出しは、現在のスレッドの割り込み、現在のスレッド()メソッドを中断し、現在のスレッドが例外:InterruptedException例外をスローしてから戻ります。
public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

空COUNTDOWN()メソッド

スレッドは、このメソッドを呼び出した後、カウンタの値がカウンタ値を下げた後、デクリメントされ、すべてのawaitメソッドは、スレッドブロックされているので、それ以外の場合は何もしない、モーニングコール場合は0です。

次見た目のカウントダウン()メソッドは、AQSをコールする方法の方法です:

public void countDown() {
    //委托sync调用AQS的方法
    sync.releaseShared(1);
}

//AQS的方法
public final boolean releaseShared(int arg) {
    //调用sync实现的tryReleaseShared
    if (tryReleaseShared(arg)) {
        //AQS的释放资源方法
        doReleaseShared();
        return true;
    }
    return false;
}

上記のコードでは、releaseShared最初のコールは、AQS同期実装方法をtryReleaseShared。

コードは以下の通りであります:

//sync的方法
protected boolean tryReleaseShared(int releases) {
    //循环进行CAS,直到当前线程成功完成CAS使计数器值(状态值state)减1并更新到state
    for (;;) {
        int c = getState();
        //如果当前状态值为0则直接返回 (1)
        if (c == 0)
            return false;
        //使用CAS让计数器值减1 (2)
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

最初の取得上記のコード現在の状態値(カウンタ値)コード(1)現在の状態が0であるかどうかを決定することは、直接カウントダウン()メソッドが戻るように、偽を返し、そうでなければ1 CASによってカウンタ値を使用して、(2)コードを実行します、 CASは再試行サイクルを失敗し、現在のカウンタ値が0であれば、それ以外の場合はtrueを返す、真の説明がある返す
最終カウントダウンスレッドが1カウンタ値に加えて、スレッド、その後、メソッドを呼び出して、だけでなく、ためたCountDownLatchののawaitメソッド呼び出しをウェイクする必要がありますスレッドがブロックされ、具体的な方法は、ブロックされたスレッドをアクティブにするdoReleaseShared AQSを呼び出すことです。

ここにコード(1)一見余計、それらは真ではない、コード(1)を添加する理由は、カウンタ値が0である場合に防止することである、とNOコード(1)、状態値がなくてもよい場合は、他のスレッドは、カウントダウンメソッドを呼び出し負になります。

注:プログラムがゼロになることはありませんので、内部カウンタたCountDownLatchエラー場合は、対応するインスタンス上のスレッドを待つことは待ち状態にあったであろう

二つの方法でこの問題を回避します:

  • 全てCountDownLatch.countDown()は、このような最終的にブロックとして、正しい位置にコードを呼び出すことを確認し
  • 使用CountDownLatch.await(ロング、TimeUnitで)この方法では、タイムアウトが、待機中のスレッドを自動的にウェイクアップする場合は、時間制限を指定するには、待機しているスレッド。

長い同様にgetCount()メソッド

カウンタの現在の値を取得しますAQSルックコードの状態の値を下回っています。

public long getCount() {
    return sync.getCount();
}

int getCount() {
    return getState();
}

コードからわかるように、又は状態(現在のカウンタ値)の値を取得するAQS getStateをメソッドの内部を呼び出します。

概要

たCountDownLatch使用AQS底が達成されます。AQSの状態変数は、カウンタ値を格納するために使用されます。まず、複数のスレッドが実際アトミック状態値を減少させる方法カウントダウンAQSを呼び出すされたCountDownLatch、初期化時の状態値(カウンタ値)を設定します。スレッドが呼び出されたときのawait現在のスレッドは、AQSは再びゼロにカウンターを遮断して待っているキューに置かれます。カウンタ値がゼロになったときに他のスレッドが現在のスレッドは、AQSのdoReleaseShared方法はスレッドをブロックされているのawait()メソッドの呼び出しによって活性化呼ぶべき、カウンタ値がLをデクリメントするためのカウントダウン方法を呼び出します。

公開された21元の記事 ウォンの賞賛8 ビュー886

おすすめ

転載: blog.csdn.net/kaihuishang666/article/details/103989428