JUCのたCountDownLatch

簡単な紹介

たCountDownLatchとCyclicBarrierをやや似ています。

カウントが与えられたCountDownLatchで初期化されます。コール#countDown()メソッドので、現在のカウントがゼロに達する前に、#を待つように、()メソッドはブロックするようにされていると思います。その後、待機中のスレッドをすべて解放します。#は、(待つ)すべての後続の呼び出しですぐに戻ります。この現象は一度だけ発生する - カウントはリセットできません。あなたがカウントをリセットする必要がある場合は、CyclicBarrierをを使用することを検討してください。

たCountDownLatchは、我々は、新しいオブジェクトに、それは、スレッドの数を意味するカウンタ値を持参する必要があるたCountDownLatchカウンタによって達成されます。

たびミッション終了後のスレッドは、カウンタがデクリメントされます。カウンタの値がゼロになると、それはすべてのスレッドがすでに回復はスレッドを継続するために、あなたは待つことができ、タスクが完了していることを意味します。、たCountDownLatchとCyclicBarrierをが似たものがあるが、彼らはしているが、まだいくつかの違いがあります。

  1. たCountDownLatch効果が実行を完了するために他のスレッドを待って許可または1つのNのスレッドです。
  2. そして、CyclicBarrierをN個のスレッドがお互いを待つために許可されています。
  3. たCountDownLatchカウンターはリセットできません。CyclicBarrierをカウンタは、使用後にリセットすることができ、それは、バリアサイクルと呼ばれています。

分析を実現

java.util.concurrent.CountDownLatch構造は、以下に示すように:

私たちは、内部たCountDownLatchは、同期が達成頼る、上記のグラフから見ることができ、および同期はAQSを継承しています。

次のようにされたCountDownLatchは、唯一のコンストラクタを提供します。

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

与えられた数と設定がされたCountDownLatchに初期化されます。

同期

変数同期、次のように定義されている同期たCountDownLatch内部クラス:

 private static final class Sync extends AbstractQueuedSynchronizer {
        
        private static final long serialVersionUID = 4982264981922014374L;

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

        // 获取同步状态
        int getCount() {
            return getState();
        }

        // 获取同步状态
        @Override
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        // 释放同步状态
        @Override
        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;
            }
        }
    }
    
}
复制代码
  1. この内部クラス実装クラスによってSyncは、我々は明らかにされたCountDownLatchを達成するために共有ロックを使用することで、見ることができます。
  2. #tryAcquireShared(INT取得)と#tryReleaseShared次の理解と一緒に(解放int)メソッド、。

待つ

たCountDownLatch次のように定義されたスレッドが中断されない限り、ラッチ前にゼロまでカウントダウン現在のスレッドが待機に#await()メソッドを提供します。

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
复制代码

AQS #acquireSharedInterruptibly(INT引数)メソッドの内部の使用方法であって、次のように

// AQS.java
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
复制代码

次のように、同期#tryAcquireShared(INT引数)メソッド、クラス内で上書きします。

// Sync.java
@Override
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
复制代码

同期状態、カウンタの値に等しい値を取得getStateを()メソッド。カウンタ値が0に等しくない場合、我々は、ここから見ることができ、(int型の引数)メソッド#doAcquireSharedInterruptibly呼び出されます。この方法は、次のようにスピン法は、同期ステータスを取得しようとしただろうです。

// AQS.java
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) {
                /**
                 * 对于CountDownLatch而言,如果计数器值不等于0,那么r 会一直小于0
                 */
                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);
    }
}
复制代码

待つ

たCountDownLatchは、スレッドが以下のように定義され、中断、またはタイムアウトを待っている場合を除き、現在のスレッドが待機ラッチ前にゼロまでカウントダウンするために、(長いタイムアウト、TimeUnitでユニット)メソッドを#awaitを提供しました。

public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
复制代码

AQSはtryAcquireSharedNanos(int型を取得し、長いnanosTimeout)メソッドを呼び出します。

カウントダウン

たCountDownLatchは、ラッチカウントダウン、#countDown()メソッドを提供しました。カウントがゼロになった場合は、すべての待機中のスレッドが起こされています。

public void countDown() {
    sync.releaseShared(1);
}
复制代码

AQSは、共有ロックの同期状態を解除する#releaseShared内部(int型引数)メソッドを呼び出します。

// AQS.java
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
复制代码

tryReleaseShared(INT引数)方法、書き換え同期たCountDownLatch内部クラス、次のように

// Sync.java
@Overrride
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        //获取锁状态
        int c = getState();
        //c == 0 直接返回,释放锁成功
        if (c == 0)
            return false;
        //计算新“锁计数器”
        int nextc = c-1;
        //更新锁状态(计数器)
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
复制代码

同様にgetCount

public long getCount() {
    return sync.getCount();
}
复制代码

概要

共有ロックによって達成内蔵されたCountDownLatch。

  1. たCountDownLatchインスタンスを作成するときは、int型のパラメータを渡す必要があります:カウントは、パラメータは、カウンタの初期値は、共有ロックの合計数を取得することができることを理解することが可能です。
  2. スレッドが#await()メソッドを呼び出すと、プログラムは、第1のカウント値がゼロであるか否かを判断し、そうでない場合はゼロ、そして、これまでに0になるまで待機する必要があります。
  3. 別のスレッドが#countDown()メソッドを呼び出すと、カウント値は、リリースに共有ロック状態行われる--1。
  4. 初期化パラメータがたCountDownLatchカウントを作成するときにすると、カウントスレッドは#countDown()メソッドを呼び出している必要がありますカウンターのカウントが0に等しいようになります、ロックが実行していきますスレッドの前で待って、リリースされます。
  5. たCountDownLatchリセットがロールバックすることはできません。

アプリケーション例

まだ会議を使用した例例。すべての5人を待つために会議室へのボスは、会議室会議に到達します。だからここに2つのスレッドがある:スレッドの上司が会議を待って、会議室のスタッフがスレッドに到着します。

public class CountDownLatchTest {

    private static CountDownLatch countDownLatch = new CountDownLatch(5);

    /**
     * Boss线程,等待员工到达开会
     */
    static class BossThread extends Thread{
        @Override
        public void run() {
            System.out.println("Boss在会议室等待,总共有" + countDownLatch.getCount() + "个人开会...");
            try {
                //Boss等待
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("所有人都已经到齐了,开会吧...");
        }
    }

    // 员工到达会议室线程
    static class EmpleoyeeThread  extends Thread{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ",到达会议室....");
            //员工到达会议室 count - 1
            countDownLatch.countDown();
        }
    }

    public static void main(String[] args){
        //Boss线程启动
        new BossThread().start();

        for(int i = 0 ; i < countDownLatch.getCount() ; i++){
            new EmpleoyeeThread().start();
        }
    }
}
复制代码

結果:

おすすめ

転載: juejin.im/post/5d8b0ffe6fb9a04ddb3b61f2