204

CountDownLatchJavaとは何か、いつ使用するべきかを理解するのを手伝ってくれる人はいますか?

このプログラムがどのように機能するかについて、私には明確な考えがありません。私が理解しているように、3 つのスレッドはすべて同時に開始され、各スレッドは 3000 ミリ秒後に CountDownLatch を呼び出します。したがって、カウントダウンは1つずつ減少します。ラッチがゼロになると、プログラムは「Completed」を出力します。私の理解の仕方が間違っていたのかもしれません。

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

class Processor implements Runnable {
    private CountDownLatch latch;

    public Processor(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        System.out.println("Started.");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}

// ------------------------------------------------ -----

public class App {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0

        ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool

        for(int i=0; i < 3; i++) {
            executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1
        }

        try {
            latch.await();  // wait until latch counted down to 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Completed.");
    }

}
4

13 に答える 13

215

はい、あなたは正しく理解しました。 CountDownLatchラッチの原則で動作し、メインスレッドはゲートが開くまで待機します。の作成時に指定されたn 個のスレッドを 1 つのスレッドが待機しますCountDownLatch

すべてのスレッド (通常はアプリケーションのメイン スレッド) を呼び出すCountDownLatch.await()と、count がゼロになるか、別のスレッドによって中断されるまで待機します。CountDownLatch.countDown()他のすべてのスレッドは、完了または準備ができたら呼び出してカウントダウンする必要があります。

カウントがゼロになるとすぐに、待機中のスレッドが続行されます。の欠点/利点の 1 つは、CountDownLatch再利用できないことです。count がゼロになるとCountDownLatch、それ以上使用できなくなります。

編集:

CountDownLatch1 つのスレッド (メイン スレッドなど) が、処理を続行する前に、1 つ以上のスレッドが完了するまで待機する必要がある場合に使用します。

Javaでの使用の古典的な例はCountDownLatch、サービス アーキテクチャを使用するサーバー側のコア Java アプリケーションです。このアーキテクチャでは、複数のサービスが複数のスレッドによって提供され、アプリケーションはすべてのサービスが正常に開始されるまで処理を開始できません。

PS OPの質問には非常に簡単な例があるため、含めませんでした。

于 2013-07-24T07:06:46.260 に答える
44

CountDownLatchJava では、処理を開始する前にThread 1 つ以上の s を待機できるシンクロナイザーの一種です。Thread

CountDownLatchラッチの原理で動作し、スレッドはゲートが開くまで待機します。n1 つのスレッドは、作成中に指定されたスレッド数だけ待機しますCountDownLatch

例えばfinal CountDownLatch latch = new CountDownLatch(3);

ここでは、カウンターを 3 に設定します。

すべてのスレッド (通常はアプリケーションのメイン スレッド) を呼び出すCountDownLatch.await()と、 count がゼロになるか、別の によって中断されるまで待機しますThreadCountDownLatch.countDown()他のすべてのスレッドは、完了またはジョブの準備ができたら、呼び出してカウントダウンを行う必要があります。カウントがゼロになるとすぐに、Thread待機が開始されます。

ここで、カウントはメソッドによってデクリメントされCountDownLatch.countDown()ます。

Threadメソッドを呼び出す は、await()初期カウントがゼロになるまで待機します。

カウントをゼロにするには、他のスレッドがcountDown()メソッドを呼び出す必要があります。カウントがゼロになると、await()メソッドを呼び出したスレッドが再開します (実行を開始します)。

の欠点CountDownLatchは、再利用できないことです。カウントがゼロになると、使用できなくなります。

于 2014-03-19T06:54:15.863 に答える
24

NikolaB はとてもよく説明しました. しかし, 例は理解するのに役立ちます. では, 簡単な例を 1 つ...

 import java.util.concurrent.*;


  public class CountDownLatchExample {

  public static class ProcessThread implements Runnable {

    CountDownLatch latch;
    long workDuration;
    String name;

    public ProcessThread(String name, CountDownLatch latch, long duration){
        this.name= name;
        this.latch = latch;
        this.workDuration = duration;
    }


    public void run() {
        try {
            System.out.println(name +" Processing Something for "+ workDuration/1000 + " Seconds");
            Thread.sleep(workDuration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+ "completed its works");
        //when task finished.. count down the latch count...

        // basically this is same as calling lock object notify(), and object here is latch
        latch.countDown();
    }
}


public static void main(String[] args) {
    // Parent thread creating a latch object
    CountDownLatch latch = new CountDownLatch(3);

    new Thread(new ProcessThread("Worker1",latch, 2000)).start(); // time in millis.. 2 secs
    new Thread(new ProcessThread("Worker2",latch, 6000)).start();//6 secs
    new Thread(new ProcessThread("Worker3",latch, 4000)).start();//4 secs


    System.out.println("waiting for Children processes to complete....");
    try {
        //current thread will get notified if all chidren's are done 
        // and thread will resume from wait() mode.
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("All Process Completed....");

    System.out.println("Parent Thread Resuming work....");



     }
  }
于 2014-01-06T17:28:53.440 に答える
23

複数のスレッドがタスクを完了するのを待ちたい場合に使用します。スレッドに参加するのと似ています。

CountDownLatch を使用できる場所

3 つのスレッド "A"、"B"、"C" があり、"A" と "B" スレッドがタスクを完了または部分的に完了したときにのみスレッド "C" を開始するという要件があるシナリオを考えてみましょう。

実際のITシナリオに適用可能

マネージャーが開発チーム (A と B) 間でモジュールを分割し、両方のチームがタスクを完了したときにのみ、テストのためにそれを QA チームに割り当てたいというシナリオを考えてみましょう。

public class Manager {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        MyDevTeam teamDevA = new MyDevTeam(countDownLatch, "devA");
        MyDevTeam teamDevB = new MyDevTeam(countDownLatch, "devB");
        teamDevA.start();
        teamDevB.start();
        countDownLatch.await();
        MyQATeam qa = new MyQATeam();
        qa.start();
    }   
}

class MyDevTeam extends Thread {   
    CountDownLatch countDownLatch;
    public MyDevTeam (CountDownLatch countDownLatch, String name) {
        super(name);
        this.countDownLatch = countDownLatch;       
    }   
    @Override
    public void run() {
        System.out.println("Task assigned to development team " + Thread.currentThread().getName());
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
                ex.printStackTrace();
        }
        System.out.println("Task finished by development team " + Thread.currentThread().getName());
        this.countDownLatch.countDown();
    }
}

class MyQATeam extends Thread {   
    @Override
    public void run() {
        System.out.println("Task assigned to QA team");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("Task finished by QA team");
    }
}

上記のコードの出力は次のようになります。

開発チーム devB に割り当てられたタスク

開発チーム devA に割り当てられたタスク

開発チーム devB がタスクを完了

開発チーム devA がタスクを完了

QA チームに割り当てられたタスク

QA チームが完了したタスク

ここでawait()メソッドは countdownlatch フラグが 0 になるのを待ち、countDown()メソッドは countdownlatch フラグを 1 減らします。

JOIN の制限: 上記の例は JOIN でも実現できますが、JOIN は次の 2 つのシナリオでは使用できません。

  1. Thread クラスの代わりに ExecutorService を使用してスレッドを作成する場合。
  2. 開発の 80% のタスクが完了したらすぐにマネージャーが QA チームにコードを引き渡したいという上記の例を変更します。これは、CountDownLatch により、部分的な実行のために別のスレッドを待機するために使用できる実装を変更できることを意味します。
于 2015-12-07T15:51:24.257 に答える
4

CoundDownLatch を使用すると、他のすべてのスレッドの実行が完了するまでスレッドを待機させることができます。

擬似コードには次のものがあります。

// Main thread starts
// Create CountDownLatch for N threads
// Create and start N threads
// Main thread waits on latch
// N threads completes there tasks are returns
// Main thread resume execution
于 2014-06-05T03:33:13.720 に答える
3

JavaDoc ( https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html ) に記載されているように、CountDownLatch は Java 5 で導入された同期支援です。ここでは、同期は行われません。クリティカルセクションへのアクセスを制限することを意味します。むしろ、異なるスレッドのアクションを順序付けます。CountDownLatch によって達成される同期のタイプは、Join のタイプと似ています。他のワーカー スレッド "T1"、"T2"、"T3" がそのタスクを完了するのを待つ必要があるスレッド "M" があると仮定します。

    T1.join();
    T2.join();
    T3.join();

上記のコードは、T1、T2、T3 が作業を完了した後に、スレッド M が作業を再開することを確認します。T1、T2、T3 は、任意の順序で作業を完了できます。同じことは、T1、T2、T3、およびスレッド M が同じ CountDownLatch オブジェクトを共有する CountDownLatch を介して実現できます。
"M" リクエスト: countDownLatch.await();
"T1","T2","T3" は countDownLatch.countdown();

join メソッドの欠点の 1 つは、M が T1、T2、T3 について知らなければならないことです。後で追加された新しいワーカー スレッド T4 がある場合、M もそれを認識している必要があります。これは、CountDownLatch で回避できます。実装後、アクションのシーケンスは [T1,T2,T3] (T1,T2,T3 の順序は何でも構いません) -> [M] になります。

于 2016-12-26T17:13:38.590 に答える
2

このようなものをいつ使用するかの 1 つの良い例は、シリアル ポートにアクセスする Java Simple Serial Connector です。通常、ポートに何かを書き込み、別のスレッドで非同期的に、デバイスは SerialPortEventListener で応答します。通常、ポートへの書き込み後に応答を待つために一時停止します。このシナリオのスレッド ロックを手動で処理するのは非常に難しいですが、Countdownlatch を使用するのは簡単です。別の方法で実行できると考える前に、思いもよらなかった競合状態に注意してください!!

擬似コード:

CountDownLatch latch;
void writeData() { 
   latch = new CountDownLatch(1);
   serialPort.writeBytes(sb.toString().getBytes())
   try {
      latch.await(4, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
   }
}
class SerialPortReader implements SerialPortEventListener {
    public void serialEvent(SerialPortEvent event) {
        if(event.isRXCHAR()){//If data is available
            byte buffer[] = serialPort.readBytes(event.getEventValue());
            latch.countDown();
         }
     }
}

于 2015-05-25T02:27:23.873 に答える