Comment CountDownLatch est-il utilisé en Java Multithreading?

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

je n'ai pas une idée très claire de la façon dont fonctionne ce programme. Comme je comprends tous les trois threads commencent à la fois et chaque Thread appellera CountDownLatch après 3000ms. Donc le compte à rebours décrémentera un par un. Après le verrouillage devient zéro le programme imprime "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.");
    }

}
143
demandé sur ROMANIA_engineer 2013-07-24 10:47:48

10 réponses

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

N'importe quel fil, habituellement le fil principal de l'application, qui appelle CountDownLatch.await() attendra jusqu'à ce que le compte atteigne zéro ou il est interrompu par un autre fil. Tous les autres threads sont nécessaires pour compter vers le bas en appelant CountDownLatch.countDown() une fois ils sont terminés ou prêts.

dès que le compte atteint zéro, le fil d'attente continue. Un des inconvénients/avantages de CountDownLatch est qu'il n'est pas réutilisable: une fois le compte atteint zéro, vous ne pouvez plus utiliser CountDownLatch .

Edit:

utiliser CountDownLatch quand un thread (comme le thread principal) nécessite d'attendre qu'un ou plusieurs threads à compléter, avant qu'il puisse continuer traitement.

un exemple classique d'utilisation de CountDownLatch en Java est une application Java de base côté serveur qui utilise l'architecture de services, où plusieurs services sont fournis par plusieurs threads et l'application ne peut pas commencer le traitement jusqu'à ce que tous les services ont commencé avec succès.

P. S. La question de L'OP a un exemple assez simple donc je ne l'ai pas inclus.

164
répondu NikolaB 2018-04-04 19:13:05

CountDownLatch en Java est un type de synchroniseur qui permet à un Thread d'attendre un ou plusieurs Thread avant de commencer le traitement.

CountDownLatch fonctionne sur le principe de verrouillage, le fil va attendre jusqu'à ce que la porte est ouverte. Un thread attend le n nombre de threads spécifié lors de la création de CountDownLatch .

p.ex. final CountDownLatch latch = new CountDownLatch(3);

ici nous plaçons le compteur à 3.

N'importe quel fil, habituellement le fil principal de l'application, qui appelle CountDownLatch.await() attendra jusqu'à ce que le compte atteigne zéro ou il est interrompu par un autre Thread . Tous les autres threads sont nécessaires pour faire le décompte en appelant CountDownLatch.countDown() une fois qu'ils sont terminés ou prêts pour le travail. dès que le compte atteint zéro, le Thread en attente commence à courir.

ici le compte est décrémenté par la méthode CountDownLatch.countDown() .

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

pour faire compter zéro autres threads besoin d'appeler la méthode countDown() . Une fois que le compte devient zéro, le thread qui a invoqué la méthode await() va reprendre (démarrer son exécution).

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

34
répondu Vishal Akkalkote 2015-05-24 07:19:30

NikolaB l'a très bien expliqué, cependant l'exemple serait utile à comprendre, donc 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
répondu vikashait 2014-01-06 17:28:53

il est utilisé lorsque nous voulons attendre plus d'un thread pour accomplir sa tâche. Il est similaire à joindre dans les fils.

où nous pouvons utiliser CountDownLatch

envisager un scénario où nous avons exigence où nous avons trois threads" A"," B "et" C "et nous voulons démarrer le thread" C "seulement lorsque" A "et" B " threads complète ou en partie leur tâche.

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

envisager un scénario où le gestionnaire divisé les modules entre les équipes de développement (A et B) et il veut l'assigner à l'équipe D'AQ pour les essais seulement lorsque les deux équipes achèvent 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");
    }
}

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 de la qualité

tâche terminée par L'équipe D'Assurance Qualité

Ici await() méthode attend pour countdownlatch drapeau de 0, et compte à rebours() méthode décrémente countdownlatch le drapeau à 1.

Limitation de la jointure: L'exemple ci-dessus peut aussi être réalisé avec la jointure, mais la jointure ne peut pas être utilisée 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 veut transférer le code à L'équipe D'assurance de la qualité dès que le développement termine leur tâche de 80%. Cela signifie que CountDownLatch nous permet de modifier l'implémentation qui peut être utilisée pour attendre un autre thread pour leur exécution partielle.
17
répondu V Jo 2015-12-11 03:29:57

un bon exemple d'utilisation de quelque chose comme ça est avec Java Simple Serial Connector, l'accès aux ports série. Typiquement vous allez écrire quelque chose sur le port, et asyncroniquement, sur un autre thread, le périphérique va répondre sur un SerialPortEventListener. Typiquement, vous voudrez faire une pause après avoir écrit sur le port pour attendre la réponse. La manipulation manuelle des verrous de thread pour ce scénario est extrêmement délicate, mais L'utilisation de Countdownlatch est facile. Avant de partir en pensant que tu peux le faire une autre façon, faites attention aux conditions de course auxquelles vous n'avez jamais pensé!!

Pseudo:


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
répondu user2709454 2015-05-25 02:27:23

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

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
répondu Christophe Roussy 2016-03-02 10:14:20

si vous ajoutez un peu de débogage après votre appel à la serrure.compte à rebours(), cela peut vous aider à comprendre son comportement mieux.

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

la sortie indiquera que le nombre est décrémenté. Ce' count ' est effectivement le nombre de tâches exécutables (objets processeurs) que vous avez lancées et pour lesquelles countDown() a invoqué et non et est donc bloqué sur le thread principal lors de son appel au verrouillage.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
répondu natmat 2016-03-18 12:06:37

de la documentation d'oracle sur CountDownLatch :

aide à la synchronisation qui permet à un ou plusieurs threads d'attendre qu'un ensemble d'opérations effectuées dans d'autres threads soit terminé.

Un CountDownLatch est initialisé avec un comte. Les méthodes await bloquent jusqu'à ce que le nombre de courant atteigne zéro en raison d'invocations de la méthode countDown() , après quoi tout les fils d'attente sont libérés et toutes les invocations subséquentes d'attente reviennent immédiatement. Il s'agit d'un phénomène à un seul coup -- le compte ne peut pas être réinitialisé.

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

A CountDownLatch initialisé avec un compte d'un Sert de simple on / off latch, ou porte: tous les fils invoquant attendre à la porte jusqu'à ce qu'elle soit ouverte par un fil invoquant compte à rebours().

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

public void await()
           throws InterruptedException

fait attendre le fil courant jusqu'à ce que le verrou ait compté jusqu'à zéro, à moins que le fil ne soit interrompu.

si le compte courant est zéro alors cette méthode retourne immédiatement.

public void countDown()

décrète le compte de la serrure, libérant tous les fils d'attente si le compte atteint zéro.

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

Explication de votre exemple.

  1. vous avez fixé le compte à 3 pour latch variable

    CountDownLatch latch = new CountDownLatch(3);
    
  2. vous avez passé ce partage latch à fil Ouvrier: Processor

  3. trois Runnable les cas de Processor ont été soumis à ExecutorService executor
  4. le fil principal ( App ) est en attente pour compter pour devenir zéro avec la déclaration ci-dessous

     latch.await();  
    
  5. Processor thread dort pendant 3 secondes puis il décrémente le comte de la valeur avec latch.countDown()
  6. première Process l'instance changera le nombre de verrous à 2 après son achèvement dû à latch.countDown() .

  7. deuxième Process l'instance changera le nombre de verrous à 1 après son achèvement dû à latch.countDown() .

  8. troisième Process l'instance changera le nombre de verrous à 0 une fois qu'elle sera terminée à cause de latch.countDown() .

  9. Zéro compter sur le loquet de causes thread principal App pour sortir de await

  10. App program imprime cette sortie maintenant: Completed

2
répondu 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 un outil de synchronisation, introduit en 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 fils. Le type de synchronisation atteint par CountDownLatch est similaire à celui de jointure. Supposons qu'il existe un fil "M" qui doit attendre que les autres threads "T1", "T2", " T3 " accomplissent leurs tâches Avant Java 1.5, la façon dont cela peut être fait est D'exécuter le code suivant

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

le code ci-dessus assure que thread M reprend son travail après T1, T2, T3 a terminé son travail. T1, T2, T3 peuvent compléter leur travail dans n'importe quel ordre. Le même résultat peut être obtenu par CountDownLatch, où T1,T2, T3 et thread m partagent le même objet CountDownLatch.

"M" demandes: countDownLatch.await();

où "T1", "T2", " T3 " fait countDownLatch.countdown();

l'un des inconvénients de la méthode jointure est que M doit connaître T1, T2, T3. Si il y a un nouveau thread T4 ajouté plus tard, alors M est d'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
répondu S R Chaitanya 2018-04-13 11:13:47

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

0
répondu Ashwin Patil 2017-09-21 06:02:02