まず、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の場合、待機していたスレッドは実行を継続します