JAVAコンカレントマルチスレッドクラス(1):CountDownLatchの理解と使用

目次

CountDownLatchの概念

CountDownLatchの使用法

CountDownLatchの欠点

CountDownLatch(カウントダウン計算機)の手順

テスト例1:

テスト2の例:


CountDownLatchの概念

CountDownLatchは、複数のスレッド間の同期を調整するため、またはスレッド間で通信するために使用される同期ツールクラスです(相互排他として機能するのではなく)。

CountDownLatchは、他のスレッドが作業を完了するのを待った後、スレッドが実行を継続できるようにします。

実装にはカウンターを使用します。カウンターの初期値はスレッド数です。各スレッドがタスクを完了すると、

カウンターの値が1つ減少します。カウンターの値が0の場合、すべてのスレッドがいくつかのタスクを完了したことを意味します。

次に、CountDownLatchで待機しているスレッドは、次のタスクの実行を再開できます。

CountDownLatchの使用法

CountDownLatchの一般的な使用方法:

1.特定のスレッドは、実行を開始する前に、n個のスレッドの実行が完了するのを待ちます。CountDownLatchのカウンターを新しいCountDownLatch(n)に初期化し、タスクスレッドがcountdownLatch.countDown()の実行を完了するたびにカウンターを1ずつ減らします。カウンターの値が0になると、CountDownLatchのawait()のスレッドは目覚めた。典型的なアプリケーションシナリオは、サービスが開始されると、メインスレッドが続行する前に複数のコンポーネントがロードされるのを待つ必要があるというものです。

2.複数のスレッドがタスクの実行を開始するときに最大の並列処理を実現します。これは並列性ではなく並列処理であり、複数のスレッドが同時に実行を開始することを強調しています。レースと同様に、複数のスレッドが開始点に配置され、開始ガンが鳴るのを待ってから、同時に実行されます。このメソッドは、共有CountDownLatch(1)を初期化し、そのカウンターを1に初期化して、タスクの実行を開始する前に最初に複数のスレッドcountdownlatch.await()を実行し、メインスレッドがcountDown()を呼び出すと、カウンターが0になり、複数のスレッド同時に目覚めました。

CountDownLatchの欠点

CountDownLatchは1回限りです。計算機の値はコンストラクタで一度しか初期化できず、後で値を再設定するメカニズムはありません。CountDownLatchが使い果たされると、再度使用することはできません。

CountDownLatch(カウントダウン計算機)の手順

メソッドの説明

public void countDown()

  ラッチのカウントを減らし、カウントがゼロに達した場合は、待機中のスレッドをすべて解放します。現在のカウントがゼロより大きい場合、カウントは減少します。

public boolean await(long timeout、TimeUnit unit)throws InterruptedException

  スレッドが中断されたり、指定された待機時間を超過したりしない限り、ラッチがゼロまでカウントダウンするまで現在のスレッドを待機させます。現在のカウントがゼロの場合、このメソッドはすぐに真の値を返します。

  現在のカウントがゼロより大きい場合、現在のスレッドはスレッドスケジューリングの目的で無効になり、次の3つの状況のいずれかが発生するまでスレッドは休止状態のままになります。

  countDown()メソッドが呼び出されたため、カウントがゼロに達した、または他のスレッドが現在のスレッドに割り込んだ、または指定された待機時間が経過した。

  • カウントがゼロに達すると、メソッドは真の値を返します。
  • 現在のスレッドがこのメソッドに入るときにすでにスレッドの中断状態を設定している場合、または待機中に中断された場合、InterruptedExceptionがスローされ、現在のスレッドの中断状態がクリアされます。
  • 指定された待機時間を超過した場合、戻り値はfalseです。時間がゼロ以下の場合、メソッドはまったく待機しません。

パラメータ:

  タイムアウト-待機する最長時間

  unit-timeoutパラメータtime unit

戻る:

  カウントがゼロになるとtrueを返し、カウントがゼロになる前に待機時間が経過するとfalseを返します。

スロー:

  InterruptedException-現在のスレッドが待機中に中断された場合

 

テスト例1:

  メインスレッドは、子スレッドの実行が完了するまで待機します。

package com.example.demo.CountDownLatchDemo;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 主线程等待子线程执行完成再执行
 */
public class CountdownLatchTest1 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(3);
        final CountDownLatch latch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
                        latch.countDown();//当前线程调用此方法,则计数减一
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
        }

        try {
            System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
            latch.await();//阻塞当前线程,直到计数器的值为0
            System.out.println("主线程"+Thread.currentThread().getName()+"开始执行...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

テスト2の例:

100メートルレースでは、4人のアスリートがフィールドに到着し、レフェリーのパスワードを待ちました。レフェリーがコマンドを発行し、プレーヤーが同時に走り始めました。すべてのプレーヤーがフィニッシュラインに到達すると、レフェリーがサマリーランキングを作成しました

package com.sapdev.hook;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final CountDownLatch cdOrder = new CountDownLatch(1);
        final CountDownLatch cdAnswer = new CountDownLatch(4);
        for (int i = 0; i < 4; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
                        cdOrder.await();
                        System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
                        cdAnswer.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
        }
        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("裁判"+Thread.currentThread().getName()+"即将发布口令");
            cdOrder.countDown();
            System.out.println("裁判"+Thread.currentThread().getName()+"已发送口令,正在等待所有选手到达终点");
            /**
			 * [依次环形同步当前任务,否则造成线程错乱执行/监测子线程的执行同步]
			 * [await()进行等待,当计数减到0时,所有线程并行执行]
			 * 注意:cdAnswer此处需要等待释放,即使上面的count已经为0了,此处需要再次尝试阻塞,如果上面为0状态,所以本次阻塞是无效的
			 */
            cdAnswer.await();
            System.out.println("所有选手都到达终点");
            System.out.println("裁判"+Thread.currentThread().getName()+"汇总成绩排名");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }
}

 

おすすめ

転載: blog.csdn.net/bj_chengrong/article/details/108703895