web-dev-qa-db-ja.com

CountDownLatchはJavaマルチスレッドでどのように使用されますか?

誰かがJava CountDownLatchとは何か、いつそれを使用するかを理解するのを助けることができますか?

このプログラムがどのように機能するかについて、私にはあまり明確な考えがありません。私が理解しているように、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.");
    }

}
164
amal

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

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

カウントがゼロになるとすぐに、待機中のスレッドが継続します。 CountDownLatchのデメリット/利点の1つは、再利用できないことです。カウントがゼロに達すると、CountDownLatchを使用できなくなります。

編集:

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

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

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

177
NikolaB

JavaのCountDownLatchは、1つのThreadが処理を開始する前に1つ以上のThreadsを待機できる同期装置の一種です。

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

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

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

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

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

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

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

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

36

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....");



     }
  }
20
vikashait

複数のスレッドがタスクを完了するまで待機する場合に使用されます。スレッドでの結合に似ています。

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を使用すると、部分実行のために別のスレッドを待機するために使用できる実装を変更できます。
17
V Jo

CountDownLatch に関するOracleドキュメントから:

他のスレッドで実行されている一連の操作が完了するまで、1つ以上のスレッドが待機できるようにする同期支援。

CountDownLatchname__は、指定されたカウントで初期化されます。 awaitname__メソッドは、countDown()メソッドの呼び出しにより現在のカウントがゼロになるまでブロックします。その後、待機中のすべてのスレッドが解放され、その後のawaitの呼び出しが直ちに返されます。これはワンショット現象です。カウントはリセットできません。

CountDownLatchは多用途の同期ツールであり、さまざまな目的に使用できます。

1のカウントで初期化されたCountDownLatchname__は、単純なオン/オフラッチ、またはゲートとして機能します。待機を呼び出すすべてのスレッドは、countDown()を呼び出すスレッドによって開かれるまで、ゲートで待機します。

Nに初期化されたCountDownLatchname__を使用すると、N個のスレッドが何らかのアクションを完了するまで、または何らかのアクションがN回完了するまで1つのスレッドを待機させることができます。

public void await()
           throws InterruptedException

スレッドが中断されない限り、ラッチがゼロまでカウントダウンするまで現在のスレッドを待機させます。

現在のカウントがゼロの場合、このメソッドはすぐに戻ります。

public void countDown()

ラッチのカウントをデクリメントし、カウントがゼロに達すると待機中のスレッドをすべて解放します。

現在のカウントがゼロより大きい場合、デクリメントされます。新しいカウントがゼロの場合、待機中のすべてのスレッドはスレッドスケジューリングのために再度有効になります。

あなたの例の説明。

  1. latchname__変数のカウントを3に設定しました

    CountDownLatch latch = new CountDownLatch(3);
    
  2. この共有latchname__をワーカースレッドに渡しました:Processorname__

  3. Runnablename__の3つのProcessorname__インスタンスがExecutorServiceexecutorname__に送信されました
  4. メインスレッド(Appname__)は、以下のステートメントでカウントがゼロになるのを待っています

     latch.await();  
    
  5. Processorname__スレッドは3秒間スリープし、その後latch.countDown()でカウント値をデクリメントします
  6. 最初のProcessname__インスタンスは、latch.countDown()が原因で完了後にラッチカウントを2に変更します。

  7. 2番目のProcessname__インスタンスは、latch.countDown()が原因で完了した後、ラッチカウントを1に変更します。

  8. 3番目のProcessname__インスタンスは、latch.countDown()が原因で完了すると、ラッチカウントを0に変更します。

  9. ラッチのゼロカウントにより、メインスレッドAppname__がawaitname__から出てきます。

  10. アプリプログラムはこの出力をすぐに出力します:Completedname__

2
Ravindra babu

Clamp.countDown()を呼び出した後にデバッグを追加すると、その動作をよりよく理解するのに役立ちます。

latch.countDown();
System.out.println("DONE "+this.latch); // Add this debug

出力には、カウントが減少していることが示されます。この「カウント」は、実際には、countDown()がnot呼び出されたためにブロックされたときに開始したRunnableタスク(Processorオブジェクト)の数ですメインスレッドは、ratch.await()の呼び出しで。

DONE Java.util.concurrent.CountDownLatch@70e69696[Count = 2]
DONE Java.util.concurrent.CountDownLatch@70e69696[Count = 1]
DONE Java.util.concurrent.CountDownLatch@70e69696[Count = 0]
2
natmat

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
2
user2203151

このようなものを使用する良い例の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();
         }
     }
}

2
user2709454

Java Doc のこの例は、概念を明確に理解するのに役立ちました。

class Driver { // ...
  void main() throws InterruptedException {
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) // create and start threads
      new Thread(new Worker(startSignal, doneSignal)).start();

    doSomethingElse();            // don't let run yet
    startSignal.countDown();      // let all threads proceed
    doSomethingElse();
    doneSignal.await();           // wait for all to finish
  }
}

class Worker implements Runnable {
  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;
  Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     this.startSignal = startSignal;
     this.doneSignal = doneSignal;
  }
  public void run() {
     try {
       startSignal.await();
       doWork();
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
  }

  void doWork() { ... }
}

図式的に言えば:

enter image description here

明らかに、CountDownLatchを使用すると、1つのスレッド(ここではDriver)が実行中のスレッド(ここではWorker)の実行が完了するまで待機できます。

1
Saurav Sahu

JavaDoc( https://docs.Oracle.com/javase/7/docs/api/Java/util/concurrent/CountDownLatch.html )で述べたように、CountDownLatchはJavaで導入された同期支援です。 5.ここで、同期とは、クリティカルセクションへのアクセスを制限することではありません。しかし、異なるスレッドのアクションを順番に並べます。 CountDownLatchによって達成される同期のタイプは、Joinのタイプと似ています。他のワーカースレッド「T1」、「T2」、「T3」がタスクを完了するのを待機する必要があるスレッド「M」があると仮定します。Java1.5より前の方法は、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();

結合方法の欠点の1つは、MがT1、T2、T3について知る必要があることです。後で追加された新しいワーカースレッドT4がある場合、Mもそれに注意する必要があります。これはCountDownLatchで回避できます。実装後、アクションのシーケンスは[T1、T2、T3]になります(T1、T2、T3の順序はとにかく可能性があります)-> [M]

1
S R Chaitanya
package practice;

import Java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch c= new CountDownLatch(3);  // need to decrements the count (3) to zero by calling countDown() method so that main thread will wake up after calling await() method 
        Task t = new Task(c);
        Task t1 = new Task(c);
        Task t2 = new Task(c);
        t.start();
        t1.start();
        t2.start();
        c.await(); // when count becomes zero main thread will wake up 
        System.out.println("This will print after count down latch count become zero");
    }
}

class Task extends Thread{
    CountDownLatch c;

    public Task(CountDownLatch c) {
        this.c = c;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(1000);
            c.countDown();   // each thread decrement the count by one 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
0
sumit

このリンクで説明されているcountDownLatchのベストリアルタイムの例 CountDownLatchExample

0
Ashwin Patil