Java concurrency: compte à rebours loquet vs barrière cyclique

Je lisais à travers le java.util.API concurrente , et a trouvé que

  • 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.
  • CyclicBarrier: aide à la synchronisation qui permet à un ensemble de threads d'attendre l'un l'autre pour atteindre un point de barrière commun.

Pour moi, les deux semblent égaux, mais je suis sûr qu'il y a beaucoup plus à cela.

Par exemple, dans CoundownLatch, the countdown value could not be reset, that can happen in the case of CyclicBarrier.

Y a-t-il une autre différence entre les deux?
Quels sont les use cases où quelqu'un voudrait réinitialiser la valeur du compte à rebours?

137
demandé sur palacsint 2010-11-12 23:26:41

12 réponses

Une différence majeure est que CyclicBarrier prend une tâche exécutable (facultative) qui est exécutée une fois que la condition de barrière commune est remplie.

, Il permet aussi d'obtenir le nombre de clients en attente à la barrière et le nombre requis pour déclencher la barrière. Une fois déclenchée, la barrière est réinitialisée et peut être utilisée à nouveau.

Pour les cas d'utilisation simples-démarrage des services, etc... un CountdownLatch est très bien. Un CyclicBarrier est utile pour des tâches de coordination plus complexes. Un un exemple d'une telle chose serait le calcul parallèle - où plusieurs sous-tâches sont impliquées dans le calcul - un peu comme MapReduce.

116
répondu Jon 2010-11-12 20:43:10

Il y a une autre différence.

Lorsque vous utilisez un CyclicBarrier, l'hypothèse est que vous spécifiez le nombre de threads en attente qui déclenchent la barrière. Si vous spécifiez 5, vous devez avoir au moins 5 threads pour appeler await().

Lorsque vous utilisez un CountDownLatch, vous spécifiez le nombre d'appels à countDown() qui se traduira par la libération de tous les threads en attente. Cela signifie que vous pouvez utiliser un CountDownLatch avec un seul thread.

"Pourquoi ferais-tu ça?", vous pouvez dire. Imaginez que vous utilisez un API mystérieuse codée par quelqu'un d'autre qui effectue des rappels. Vous voulez qu'un de vos threads attende qu'un certain rappel ait été appelé plusieurs fois. Vous n'avez aucune idée des threads sur lesquels le rappel sera appelé. Dans ce cas, un CountDownLatch est parfait, alors que je ne peux penser à aucun moyen de l'implémenter en utilisant un CyclicBarrier (en fait, je peux, mais cela implique des délais d'attente... beurk!).

Je souhaite juste que CountDownLatch puisse être réinitialisé!

108
répondu Kim 2011-03-01 04:53:33

Un point que personne n'a encore mentionné, c'est que, dans un CyclicBarrier, si un thread a un problème (délai d'attente, interrompu...), tous les autres qui ont atteint await() obtiennent une exception. Voir Javadoc:

Le CyclicBarrier utilise un modèle de rupture tout-ou-Aucun pour les tentatives de synchronisation échouées: si un thread quitte un point de barrière prématurément en raison d'une interruption, d'un échec ou d'un délai d'attente, tous les autres threads en attente à ce point de barrière partiront également anormalement via BrokenBarrierException (ou InterruptedException si elles aussi ont été interrompues à peu près au même moment).

34
répondu Chirlo 2012-10-07 16:03:02

Je pense que JavaDoc a expliqué explicitement les différences. La plupart des gens savent que CountDownLatch ne peut pas être réinitialisé, cependant, CyclicBarrier peut. Mais ce n'est pas la seule différence, ou le CyclicBarrier pourrait être renommé en ResetbleCountDownLatch. Nous devrions dire les différences du point de vue de leurs objectifs, qui sont décrits dans JavaDoc

CountDownLatch: aide à la synchronisation qui permet à un ou plusieurs threads d'attendre qu'un ensemble d'opérations soit effectué dans d'autres threads se termine.

CyclicBarrier: aide à la synchronisation qui permet à un ensemble de threads d'attendre l'un l'autre pour atteindre un point de barrière commun.

Dans countDownLatch, il y a un ou plusieurs threads en attente d'un ensemble de autres threads pour terminer. Dans cette situation, il existe deux types de threads, un type attend, un autre type fait quelque chose, après avoir terminé leurs tâches, ils pourraient attendre ou simplement se terminer.

Dans CyclicBarrier, il n'y a qu'un seul type de threads, ils s'attendent, ils sont égaux.

19
répondu Justin Civi 2015-04-11 10:02:48

La principale différence est documentée directement dans les Javadocs pour CountdownLatch. À savoir:

Un CountDownLatch est initialisé avec un compte tenu de comte. L'attendent méthodes bloc jusqu'à ce que le nombre actuel atteigne zéro en raison des invocations du compte à rebours() méthode, après quoi tout en attente les threads sont libérés et tout des appels successifs de attendent le retour immédiatement. C'est un one-shot phénomène -- le nombre ne peut pas être réinitialiser. Si vous avez besoin d'une version réinitialise le Compter, envisager d'utiliser un CyclicBarrier.

Source 1.6 Javadoc

12
répondu JohnnyO 2010-11-12 20:44:11

Cette question a déjà reçu une réponse adéquate, mais je pense que je peux ajouter un peu de valeur en postant du code.

Pour illustrer le comportement de la barrière cyclique, j'ai fait un exemple de code. Dès que la barrière est basculée, elle estautomatiquement réinitialisée afin qu'elle puisse être réutilisée (donc elle est "cyclique"). Lorsque vous exécutez le programme, observez que les sorties d'impression "let's play" ne sont déclenchées qu'après le basculement de la barrière.

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierCycles {

    static CyclicBarrier barrier;

    public static void main(String[] args) throws InterruptedException {
        barrier = new CyclicBarrier(3); 

        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
        Thread.sleep(1000);

        System.out.println("Barrier automatically resets.");

        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
    }

}


class Worker extends Thread {
    @Override
    public void run() {
        try {
            CyclicBarrierCycles.barrier.await();
            System.out.println("Let's play.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}
10
répondu Kevin Lee 2014-04-12 06:24:56

Un CountDownLatch est utilisé pour une synchronisation unique. Lors de l'utilisation D'un CountDownLatch, n'importe quel thread est autorisé à appeler countDown() autant de fois qu'il le souhaite. Les Threads qui ont appelé await() sont bloqués jusqu'à ce que le nombre atteigne zéro à cause des appels à countDown () par d'autres threads non bloqués. Le javadoc pour CountDownLatch États:

Le bloc de méthodes await jusqu'à ce que le nombre actuel atteigne zéro en raison de invocations de la méthode countDown (), après quoi toutes les threads sont libérés et toutes les invocations ultérieures de await return immédiatement. ...

Une autre utilisation typique serait de diviser un problème en N parties, décrivez chaque partie avec un exécutable qui exécute cette partie et compte à rebours sur le loquet, et file d'attente tous les Runnables à un exécuteur. Lorsque toutes les sous-parties sont complètes, le thread de coordination pourra pour passer à travers attendre. (Lorsque les threads doivent compter à plusieurs reprises dans de cette façon, au lieu d'utiliser un CyclicBarrier.)

En revanche, la barrière cyclique est utilisée pour plusieurs points de sychronisation, par exemple si un ensemble de threads exécute un calcul en boucle/phasé et doivent se synchroniser avant de commencer l'itération/phase suivante. Selon le javadoc pour CyclicBarrier :

La barrière est appelée cyclique car elle peut être réutilisée après la les threads en attente sont libérés.

Contrairement à CountDownLatch, chaque appel à await () appartient à une phase et peut provoque le blocage du thread jusqu'à ce que toutes les parties appartenant à cette phase aient invoqué await (). Il n'y a pas d'opération countDown() explicite prise en charge par CyclicBarrier.

9
répondu shams 2012-04-30 02:02:22

En un mot, afin de comprendre la touche fonctionnelle différences entre les deux :

public class CountDownLatch {
    private Object mutex = new Object();
    private int count;

    public CountDownLatch(int count) {
        this.count = count;
    }

    public void await() throws InterruptedException {
        synchronized (mutex) {
            while (count > 0) {
                mutex.wait();
            }
        }
    }

    public void countDown() {
        synchronized (mutex) {
            if (--count == 0)
                mutex.notifyAll();
        }

    }
}

Et

public class CyclicBarrier {
    private Object mutex = new Object();
    private int count;

    public CyclicBarrier(int count) {
        this.count = count;
    }

    public void await() throws InterruptedException {
        synchronized (mutex) {
            count--;
            while(count > 0)
                mutex.wait();
            mutex.notifyAll();
        }
    }
}

Sauf, bien sûr, des fonctionnalités telles que l'attente non bloquante, chronométrée, les diagnostics et tout ce qui a été expliqué en détail dans les réponses ci-dessus.

Les classes ci-dessus sont cependant entièrement fonctionnelles et équivalentes, dans la fonctionnalité fournie, à leurs homonymes correspondants.

Sur une note différente, la classe interne de CountDownLatch sous-classes AQS, tandis que CyclicBarrier utilise ReentrantLock (Mon soupçon est qu'il pourrait être autrement ou les deux pourraient utiliser AQS ou les deux utilisent Lock-sans aucune perte d'efficacité de performance)

5
répondu igor.zh 2017-01-23 21:56:55

Quand j'étudiais les verrous et les cyclicbarriers, j'ai trouvé ces métaphores. cyclicbarriers : imaginez qu'une entreprise dispose d'une salle de réunion. Pour commencer la réunion, un certain nombre de participants à la réunion doivent venir à la réunion (pour la rendre officielle). ce qui suit est le code d'un participant à une réunion normale (un employé)

class MeetingAtendee implements Runnable {

CyclicBarrier myMeetingQuorumBarrier;

public MeetingAtendee(CyclicBarrier myMileStoneBarrier) {
    this.myMeetingQuorumBarrier = myMileStoneBarrier;
}

@Override
public void run() {
    try {
        System.out.println(Thread.currentThread().getName() + " i joined the meeting ...");
        myMeetingQuorumBarrier.await();
        System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        System.out.println("Meeting canceled! every body dance <by chic band!>");
    }
 }
}

L'employé se joint à la réunion, attend que d'autres viennent pour commencer la réunion. en outre, il est sorti si la réunion est annulée:) alors nous avons Le patron comment les doses n'aiment pas attendre que les autres se présentent et s'il perd son patient, il annule la réunion.

class MeetingAtendeeTheBoss implements Runnable {

CyclicBarrier myMeetingQuorumBarrier;

public MeetingAtendeeTheBoss(CyclicBarrier myMileStoneBarrier) {
    this.myMeetingQuorumBarrier = myMileStoneBarrier;
}

@Override
public void run() {
    try {
        System.out.println(Thread.currentThread().getName() + "I am THE BOSS - i joined the meeting ...");
        //boss dose not like to wait too much!! he/she waits for 2 seconds and we END the meeting
        myMeetingQuorumBarrier.await(1,TimeUnit.SECONDS);
        System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        System.out.println("what WHO canceled The meeting");
    } catch (TimeoutException e) {
        System.out.println("These employees waste my time!!");
    }
 }
}

Un jour normal, l'employé vient à la réunion attendre que d'autres se présentent et si certains participants ne viennent pas, ils doivent attendre indéfiniment! dans certains réunion spéciale le patron arrive et il n'aime pas attendre.(5 personnes doivent commencer à se rencontrer mais seul le patron vient et aussi un employé enthousiaste) donc il annule la réunion (avec colère)

CyclicBarrier meetingAtendeeQuorum = new CyclicBarrier(5);
Thread atendeeThread = new Thread(new MeetingAtendee(meetingAtendeeQuorum));
Thread atendeeThreadBoss = new Thread(new MeetingAtendeeTheBoss(meetingAtendeeQuorum));
    atendeeThread.start();
    atendeeThreadBoss.start();

Sortie:

//Thread-1I am THE BOSS - i joined the meeting ...
// Thread-0 i joined the meeting ...
// These employees waste my time!!
// Meeting canceled! every body dance <by chic band!>

Il existe un autre scénario dans lequel un autre thread externe (un tremblement de terre) annule la réunion (méthode de réinitialisation d'appel). dans ce cas, tous les threads en attente sont réveillés par une exception.

class NaturalDisasters implements Runnable {

CyclicBarrier someStupidMeetingAtendeeQuorum;

public NaturalDisasters(CyclicBarrier someStupidMeetingAtendeeQuorum) {
    this.someStupidMeetingAtendeeQuorum = someStupidMeetingAtendeeQuorum;
}

void earthQuakeHappening(){
    System.out.println("earth quaking.....");
    someStupidMeetingAtendeeQuorum.reset();
}

@Override
public void run() {
    earthQuakeHappening();
 }
}

L'exécution du code entraînera une sortie amusante:

// Thread-1I am THE BOSS - i joined the meeting ...
// Thread-0 i joined the meeting ...
// earth quaking.....
// what WHO canceled The meeting
// Meeting canceled! every body dance <by chic band!>

Vous pouvez également ajouter une secrétaire à la salle de réunion, si une réunion a lieu, elle documentera tout mais elle ne fait pas partie de la réunion:

class MeetingSecretary implements Runnable {

@Override
public void run() {
        System.out.println("preparing meeting documents");
        System.out.println("taking notes ...");
 }
}

Verrous : si le patron en colère veut organiser une exposition pour les clients de l'entreprise, tout doit être prêt (ressources). nous fournissons une liste de tâches chaque travailleur (Thread) dose son travail et nous vérifions la liste de tâches (certains travailleurs font de la peinture, d'autres préparent le système de son...). lorsque tous les éléments de liste sont complètes (ressources), nous pouvons ouvrir les portes aux clients.

public class Visitor implements Runnable{

CountDownLatch exhibitonDoorlatch = null;

public Visitor (CountDownLatch latch) {
    exhibitonDoorlatch  = latch;
}

public void run() {
    try {
        exhibitonDoorlatch .await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("customer visiting exebition");
 }
}

Et les travailleurs comment préparent l'exposition:

class Worker implements Runnable {

CountDownLatch myTodoItem = null;

public Worker(CountDownLatch latch) {
    this.myTodoItem = latch;
}

public void run() {
        System.out.println("doing my part of job ...");
        System.out.println("My work is done! remove it from todo list");
        myTodoItem.countDown();
 }
}

    CountDownLatch preperationTodoList = new CountDownLatch(3);

    // exhibition preparation workers  
    Worker      electricalWorker      = new Worker(preperationTodoList);
    Worker      paintingWorker      = new Worker(preperationTodoList);

    // Exhibition Visitors 
    ExhibitionVisitor exhibitionVisitorA = new ExhibitionVisitor(preperationTodoList);
    ExhibitionVisitor exhibitionVisitorB = new ExhibitionVisitor(preperationTodoList);
    ExhibitionVisitor exhibitionVisitorC = new ExhibitionVisitor(preperationTodoList);

    new Thread(electricalWorker).start();
    new Thread(paintingWorker).start();

    new Thread(exhibitionVisitorA).start();
    new Thread(exhibitionVisitorB).start();
    new Thread(exhibitionVisitorC).start();
5
répondu NԀƎ 2017-08-09 11:31:12

Dans le cas de CyclicBarrier, dès que tous les threads enfants commencent à appeler barrier.await (), le Runnable est exécuté dans la barrière. Obstacle.attendre dans chaque fil enfant prendra différentes longueurs de temps pour finir, et ils finissent tous en même temps.

4
répondu Brandon 2011-12-14 15:26:29

Une différence évidente est que seuls N threads peuvent attendre sur un CyclicBarrier de N pour être libérés en un cycle. Mais un nombre illimité de threads peut attendre sur un CountDownLatch de N. le décompte peut être fait par un thread N fois ou N threads une fois chacun ou des combinaisons.

4
répondu Pramma 2015-10-29 17:21:07

Dans CountDownLatch , les threads principaux attendent que les autres threads terminent leur exécution. Dans CyclicBarrier , les threads de travail attendent les uns des autres pour terminer leur exécution.

Vous ne pouvez pas réutiliser la même instance CountDownLatch Une fois que le nombre atteint à zéro et que le verrou est ouvert, d'autre part CyclicBarrier peut être réutilisé en réinitialisant Barrier, une fois la barrière cassée.

1
répondu V Jo 2016-05-14 18:38:13