Comment CountDownLatch est-il utilisé dans le Multithreading Java?


Quelqu'un Peut-il m'aider à comprendre ce que Java CountDownLatch est et quand l'utiliser?

Je n'ai pas une idée très claire du fonctionnement de ce programme. Si je comprends bien, les trois threads commencent à la fois et chaque thread appellera CountDownLatch après 3000 ms. Le compte à rebours diminuera donc un par un. Après loquet devient zéro, le programme affiche "Terminé". Peut-être que la façon dont j'ai compris est incorrecte.

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

}
Author: ROMANIA_engineer, 2013-07-24

10 answers

Oui, vous avez bien compris. CountDownLatch fonctionne en principe de verrouillage, le fil principal attendra que la porte soit ouverte. Un thread attend n threads, spécifié lors de la création du CountDownLatch.

Tout thread, généralement le thread principal de l'application, qui appelle CountDownLatch.await() attendra que le nombre atteigne zéro ou qu'il soit interrompu par un autre thread. Tous les autres threads doivent décompter en appelant CountDownLatch.countDown() une fois qu'ils sont terminés ou prêts.

Dès que le nombre atteint zéro, le thread en attente continue. L'un des inconvénients/avantages de CountDownLatch est qu'il n'est pas réutilisable: une fois que count atteint zéro, vous ne pouvez plus utiliser CountDownLatch.

Modifier:

Utilisez CountDownLatch lorsqu'un thread (comme le thread principal) doit attendre la fin d'un ou plusieurs threads avant de pouvoir continuer le traitement.

Un exemple classique d'utilisation de CountDownLatch en Java est une application Java côté serveur qui utilise une architecture de services, où plusieurs services sont fournis par plusieurs threads et l'application ne peut pas démarrer le traitement tant que tous les services n'ont pas démarré avec succès.

P.s. La question d'OP a un exemple assez simple, donc je n'en ai pas inclus.

 164
Author: NikolaB, 2018-04-04 19:13:05

CountDownLatch en Java est un type de synchroniseur qui permet Thread attendre pour un ou plusieurs Threads avant de lancer le traitement.

CountDownLatch fonctionne sur le principe du verrou, le fil attendra jusqu'à ce que la porte soit ouverte. Un thread attend n nombre de threads spécifiés lors de la création de CountDownLatch.

Par exemple final CountDownLatch latch = new CountDownLatch(3);

Ici, nous mettons le compteur à 3.

Tout thread, généralement le thread principal de l'application, qui appelle {[7] } attendra que le nombre atteigne zéro ou qu'il soit interrompu par un autre Thread. Tous les autres threads sont tenus de faire le compte à rebours en appelant CountDownLatch.countDown() une fois qu'ils sont terminés ou prêts à la tâche. dès que count atteint zéro, le Thread en attente commence à s'exécuter.

Ici, le nombre est décrémenté par la méthode CountDownLatch.countDown().

Le Thread qui appelle la méthode await() attendra que le nombre initial atteigne zéro.

Pour faire compter zéro, les autres threads doivent appeler la méthode countDown(). Une fois que le nombre devient zéro le thread qui a appelé le await() la méthode reprendra (commence son exécution).

L'inconvénient de CountDownLatch est qu'il n'est pas réutilisable: une fois le nombre devenu zéro, il n'est plus utilisable.

 34
Author: Vishal Akkalkote, 2015-05-24 07:19:30

NikolaB l'a très bien expliqué, Mais un exemple serait utile à comprendre, Alors voici un exemple simple...

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



     }
  }
 18
Author: vikashait, 2014-01-06 17:28:53

Il est utilisé lorsque nous voulons attendre que plus d'un thread termine sa tâche. Il est similaire à se joindre à des threads.

Où nous pouvons utiliser CountDownLatch

Considérons un scénario où nous avons une exigence où nous avons trois threads "A", "B" et "C" et nous voulons démarrer le thread "C" uniquement lorsque les threads "A" et "B" terminent ou terminent partiellement leur tâche.

Il peut être appliqué au scénario informatique du monde réel

Considérons un scénario où manager divisé modules entre les équipes de développement (A et B) et il veut l'affecter à l'équipe d'assurance qualité pour tester uniquement lorsque les deux équipes ont terminé leur tâche.

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

La sortie du code ci-dessus sera:

Tâche assignée à l'équipe de développement devB

Tâche assignée à l'équipe de développement devA

Tâche terminée par l'équipe de développement devB

Tâche terminée par l'équipe de développement devA

Tâche assignée à l'équipe d'assurance qualité

Tâche terminée par l'équipe d'assurance qualité

Ici La méthode await () attend que l'indicateur countdownlatch devienne 0, et la méthode countDown () décrémente l'indicateur countdownlatch de 1.

Limitation de JOINTURE: L'exemple ci-dessus peut également être réalisé avec JOIN, mais JOIN ne peut pas être utilisé dans deux scénarios:

  1. Lorsque nous utilisons ExecutorService au lieu de Thread class pour créer des threads.
  2. Modifier l'exemple ci-dessus où le gestionnaire souhaite transférer du code à l'équipe d'assurance qualité dès que le développement a terminé sa tâche à 80%. Cela signifie ce CountDownLatch nous permet de modifier l'implémentation qui peut être utilisée pour attendre un autre thread pour leur exécution partielle.
 17
Author: V Jo, 2015-12-11 03:29:57

Un bon exemple de quand utiliser quelque chose comme ça est avec Java Simple Serial Connector, l'accès aux ports série. En règle générale, vous allez écrire quelque chose sur le port, et de manière asynchrone, sur un autre thread, le périphérique répondra sur un SerialPortEventListener. En règle générale, vous devrez faire une pause après avoir écrit sur le port pour attendre la réponse. La gestion manuelle des verrous de thread pour ce scénario est extrêmement délicate, mais l'utilisation de Countdownlatch est facile. Avant de penser que vous pouvez le faire un autre attention aux conditions de course auxquelles vous n'avez jamais pensé!!

Pseudo-code:


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
Author: user2709454, 2015-05-25 02:27:23

CoundDownLatch vous permet de faire attendre un thread jusqu'à ce que tous les autres threads aient terminé leur exécution.

Le pseudo-code peut être:

// 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
Author: Christophe Roussy, 2016-03-02 10:14:20

Si vous ajoutez du débogage après votre appel à latch.countDown(), cela peut vous aider à mieux comprendre son comportement.

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

La sortie affichera le nombre en cours de décrémentation. Ce 'count' est effectivement le nombre de tâches exécutables (objets processeur) que vous avez démarrées contre lesquelles countDown() a pas été invoqué et est donc bloqué le thread principal lors de son appel à latch.attendre().

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
Author: natmat, 2016-03-18 12:06:37

À Partir de la documentation d'oracle à propos de CountDownLatch:

Aide à la synchronisation qui permet à un ou plusieurs threads d'attendre la fin d'un ensemble d'opérations effectuées dans d'autres threads.

Un CountDownLatch est initialisé avec un comte. Les méthodes await bloquent jusqu'à ce que le nombre actuel atteigne zéro en raison des invocations de la méthode countDown(), après quoi tous les threads en attente sont libérés et toutes les invocations ultérieures de await reviennent immédiatement. C'est un phénomène d'un seul coup the le compte ne peut pas être réinitialisé.

Un CountDownLatch est un outil de synchronisation polyvalent et peut être utilisé à plusieurs fins.

Un CountDownLatch initialisé avec un nombre de un sert de simple verrou on/off, ou de porte: tous les threads invoquant await wait à la porte jusqu'à ce qu'elle soit ouverte par un thread invoquant countDown().

Un CountDownLatch initialisé à N peut être utilisé pour faire attendre un thread jusqu'à ce que N threads aient terminé une action, ou action N fois.

public void await()
           throws InterruptedException

Fait attendre le thread actuel jusqu'à ce que le loquet ait compté jusqu'à zéro, à moins que le thread ne soit interrompu.

Si le nombre actuel est nul, cette méthode revient immédiatement.

public void countDown()

Décrémente le nombre du verrou, libérant tous les threads en attente si le nombre atteint zéro.

Si le nombre actuel est supérieur à zéro, il est décrémenté. Si le nouveau nombre est nul alors tous les threads en attente sont réactivés à des fins de planification des threads.

Explication de votre exemple.

  1. , Vous avez défini compter que 3 pour latch variable

    CountDownLatch latch = new CountDownLatch(3);
    
  2. Vous avez passé cette partagée latch pour thread de travail : Processor

  3. Trois Runnable instances de Processor ont été soumis à ExecutorService executor
  4. Le thread principal (App ) attend que le nombre devienne zéro avec l'instruction ci-dessous

     latch.await();  
    
  5. Processor le thread dort pendant 3 secondes, puis il décrémente la valeur de comptage avec latch.countDown()
  6. La première instance Process changera le nombre de verrous en tant que 2 après son achèvement en raison de latch.countDown().

  7. La deuxième instance Process changera le nombre de verrous en tant que 1 après son achèvement en raison de latch.countDown().

  8. La troisième instance Process changera le nombre de verrous en tant que 0 après son achèvement en raison de latch.countDown().

  9. Le compte zéro sur le verrou fait sortir le thread principal App de await

  10. Application programme imprime cette sortie maintenant : Completed

 2
Author: Ravindra babu, 2016-07-13 14:11:24

Comme mentionné dans JavaDoc (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html), CountDownLatch est une aide à la synchronisation, introduite dans Java 5. Ici, la synchronisation ne signifie pas restreindre l'accès à une section critique. Mais plutôt séquencer les actions de différents threads. Le type de synchronisation réalisé par CountDownLatch est similaire à celui de Join. Supposons qu'il y ait un thread " M " qui doit attendre un autre travailleur threads "T1"," T2"," T3 " pour terminer ses tâches Avant Java 1.5, la façon dont cela peut être fait est, M exécutant le code suivant

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

Le code ci-dessus s'assure que le thread M reprend son travail après que T1, T2, T3 ait terminé son travail. T1, T2, T3 peuvent compléter leur travail dans n'importe quel ordre. La même chose peut être obtenue via CountDownLatch, où T1, T2, T3 et thread M partagent le même objet CountDownLatch.
Demande "M": countDownLatch.await();
où comme"T1","T2"," T3 " fait countDownLatch.countdown();

Un inconvénient avec la méthode join est que M doit connaître T1, T2, T3. S'il y a un nouveau thread de travail T4 ajouté plus tard, M doit également en être conscient. Cela peut être évité avec CountDownLatch. Après la mise en œuvre, la séquence d'action serait [T1, T2, T3](l'ordre de T1,T2,T3 pourrait être de toute façon) - > [M]

 1
Author: S R Chaitanya, 2018-04-13 11:13:47

Meilleur exemple en temps réel pour countDownLatch expliqué dans ce lien CountDownLatchExample

 0
Author: Ashwin Patil, 2017-09-21 06:02:02