53-CountDownLatchはスレッドの実行順序をどのように調整しますか?

まず、JDKが提供する並行プロセス制御用のツールクラスであるCountDownLatchを紹介します。これはjava.util.concurrentパッケージに含まれており、JDK1.5の後に追加されています。主に使用されるシナリオを説明するために例を見てみましょう。

たとえば、急流に乗って遊園地に行くと、遊園地に人が少ない場合がありますが、その際、管理者から「しばらく待って、満員になるまで待ってから出航」と言われます。 。このように、あなたはある程度お金を節約することができます。遊園地の費用。座席数と同じ数の人がいます。これがCountDownLatchの基本的な考え方です。設定値に達したときにのみ開始できます。

フローチャート

フローチャートで急進の例を表現しましょう。
ここに画像の説明を挿入します
最初はCountDownLatchの初期値が3に設定されており、T0スレッドが起動するとawaitメソッドを呼び出します。その機能はこのスレッドを開始させることです。待機し、後でT1、T2、およびT3を待機し、countDownメソッドを呼び出すたびに、3の値は1ずつ減少します。つまり、3から2、2から1、1から0になります。は0に減少し、このT0は、トリガーが実行を継続するための条件に達することと同等であるため、実行を再開します。

主な方法の紹介

CountDownLatchの主なメソッドは次のとおりです。

(1)コンストラクター: public CountDownLatch(int count){};

そのコンストラクターはパラメーターを渡すことであり、パラメーター数はカウントダウンする必要がある値です。

**(2)await():** await()メソッドを呼び出すスレッドは待機を開始し、カウントダウンが終了するまで、つまりカウント値が0になるまで実行を継続しません。

(3)await(long timeout、TimeUnit unit): await()にはオーバーロードされたメソッドがあり、タイムアウトパラメーターを渡します。このメソッドはawait()に似ていますが、ここでタイムアウト期間を設定できます。もう一度待ちます。

**(4)countDown():**値を1カウントダウンします。つまり、カウントの値から1を引いて、0になると、前に待機していたスレッドが呼び出されます。

使用法

次に、CountDownLatchの2つの典型的な使用法を紹介しましょう。

使用法1:1つのスレッドは、他の複数のスレッドの完了を待ってから作業を続行します

実際のシナリオでは、多くの場合、一連の前提条件(接続の確立、データの準備など)を初期化する必要があります。これらの前提条件が完了する前に、次のステップを実行できないため、これはCountDownLatchを使用する非常に重要な方法です。 。良いシナリオです。他のスレッドの準備ができた後、アプリケーションのメインスレッドに実行を継続させることができます。

人生の例を挙げると、アスリートが走っているシーンです。たとえば、5人のアスリートがレースに参加し、フィニッシュラインに審判がいる場合、レースはいつ終了しますか?つまり、全員がフィニッシュラインに到達したとき、これは、レフリーが5人のアスリート全員がフィニッシュラインに到達してゲームの終了を発表するのを待つことと同じです。コードの形式を使用して、アスリートのランニングシーンを記述します。コードは次のとおりです。

public class RunDemo1 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        CountDownLatch latch = new CountDownLatch(5);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
    
    
            final int no = i + 1;
            Runnable runnable = new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println(no + "号运动员完成了比赛。");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        latch.countDown();
                    }
                }
            };
            service.submit(runnable);
        }
        System.out.println("等待5个运动员都跑完.....");
        latch.await();
        System.out.println("所有人都跑完了,比赛结束。");
    }
}

このコードでは、初期値が5の新しいCountDownLatchを作成し、次に固定5スレッドのスレッドプールを作成し、forループを使用してこのスレッドプールに5つのタスクを送信します。各タスクはアスリートを表します。このアスリート最初にランダムに一定時間待機して、彼が走っていることを示し、次に彼がレースを完了したことを印刷します。走った後、countDownメソッドも呼び出されてカウントが1減少します。

その後、メインスレッドに戻ります。メインスレッドが「5人のアスリート全員が走るのを待っています」という文を出力した後、await()メソッドを呼び出します。これは、メインスレッドが待機を開始することを意味します。 -待機前のスレッドが実行されます。待機が終了すると、全員がレースを終了したと見なされます。このプログラムの実行結果は次のとおりです。

等待5个运动员都跑完.....
4号运动员完成了比赛。
3号运动员完成了比赛。
1号运动员完成了比赛。
5号运动员完成了比赛。
2号运动员完成了比赛。
所有人都跑完了,比赛结束。

メインスレッドは5人のプレイヤーがゲームを完了するまで継続せず、子スレッドの待機時間はランダムであるため、各プレイヤーがゲームを完了する順序もランダムであることがわかります。

使用法2:複数のスレッドが特定のスレッドのシグナルを待機し、同時に実行を開始します

これは最初の使用法とは少し逆です。別の実際のシナリオを考えてみましょう。たとえば、スポーツ会議で、レフリーや他のアスリートについては前述したと言いましたが、現在はアスリートや他のレフリーです。アスリートが走り始める前に、レフリーが命令を出し、アスリートに統一されたスタートを与えるのを待ちます。以下に示すように、この問題を説明するためにコードを使用します。

public class RunDemo2 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        System.out.println("运动员有5秒的准备时间");
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
    
    
            final int no = i + 1;
            Runnable runnable = new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    System.out.println(no + "号运动员准备完毕,等待裁判员的发令枪");
                    try {
    
    
                        countDownLatch.await();
                        System.out.println(no + "号运动员开始跑步了");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            };
            service.submit(runnable);
        }
        Thread.sleep(5000);
        System.out.println("5秒准备时间已过,发令枪响,比赛开始!");
        countDownLatch.countDown();
    }
}

このコードでは、最初にアスリートの準備時間が5秒であることを出力してから、逆数が1のみの新しいCountDownLatchを作成します。次に、これも5スレッドスレッドプールであり、forループで送信します。 。5つのタスク、およびこれらの5つのタスクにより、await()メソッドを呼び出して、最初から待機を開始できます。

次に、メインスレッドに戻ります。メインスレッドは最初に5秒間待機します。これは、レフリーが準備をしていることを意味します。たとえば、レフリーは「すべての場所で準備ができました」と叫び、5秒後、メインスレッドは「5秒の準備時間」を出力します。が経過すると、「スターターピストルが鳴り、ゲームが開始します」という信号が表示され、countDownメソッドが呼び出されます。メインスレッドがこのメソッドを呼び出すと、await()メソッドを呼び出した5つのスレッドがすべて起動されるため、実行中です。このプログラムの結果は次のとおりです。

运动员有5秒的准备时间
2号运动员准备完毕,等待裁判员的发令枪
1号运动员准备完毕,等待裁判员的发令枪
3号运动员准备完毕,等待裁判员的发令枪
4号运动员准备完毕,等待裁判员的发令枪
5号运动员准备完毕,等待裁判员的发令枪
5秒准备时间已过,发令枪响,比赛开始!
2号运动员开始跑步了
1号运动员开始跑步了
5号运动员开始跑步了
4号运动员开始跑步了
3号运动员开始跑步了

アスリートには最初に5秒間の準備時間があり、次に5人のアスリートがスターターピストルが発射されるのを待つ準備ができており、5秒後にスターターピストルが発射されてゲームが開始されることがわかります。 5つのサブスレッドがほぼ同時に実行を開始します。

注意点

CountDownLatchの注意点について話しましょう:

  • 2つの使用法について説明しましたが、実際には、これら2つの使用法は分離されていません。2つの使用法を組み合わせることもできます。たとえば、2つのCountDownLatchを使用して、最初の初期値を複数にし、2番目の初期値を1にすることができます。このようにして、より複雑なビジネスシナリオに対処できます。
  • CountDownLatchは再利用できません。たとえば、カウントダウンが完了した場合、次回もカウントダウンを続行できますか?これは不可能です。この要件がある場合は、CyclicBarrierを使用するか、新しいCountDownLatchインスタンスを作成することを検討できます。
総括する

CountDownLatchクラスのインスタンスを作成するときは、コンストラクターでカウントダウン番号を渡す必要があります。次に、待機する必要のあるスレッドがawaitメソッドを呼び出して待機を開始し、他のスレッドがcountDownメソッドを呼び出すたびにカウントが行われます。デクリメントされるまで1ずつデクリメントされます0の場合、待機していたスレッドは実行を継続します

おすすめ

転載: blog.csdn.net/Rinvay_Cui/article/details/111056673