CountDownLatch vs Sémaphore

Y a-t-il un avantage à utiliser

java.util.concurrent.CountdownLatch

au lieu de

java.util.concurrent.Semaphore ?

Autant que je sache, les fragments suivants sont presque équivalents:

1. Sémaphore

final Semaphore sem = new Semaphore(0); for (int i = 0; i < num_threads; ++ i) { Thread t = new Thread() { public void run() { try { doStuff(); } finally { sem.release(); } } }; t.start(); } sem.acquire(num_threads); 

2: CountDownLatch

 final CountDownLatch latch = new CountDownLatch(num_threads); for (int i = 0; i < num_threads; ++ i) { Thread t = new Thread() { public void run() { try { doStuff(); } finally { latch.countDown(); } } }; t.start(); } latch.await(); 

Sauf que dans le cas n ° 2, le verrou ne peut pas être réutilisé et, plus important encore, vous devez savoir à l’avance combien de threads seront créés (ou attendre qu’ils soient tous démarrés avant de créer le verrou).

Alors, dans quelle situation le verrou pourrait-il être préférable?

    Le loquet CountDown est fréquemment utilisé pour l’exact opposé de votre exemple. En général, vous auriez beaucoup de threads bloquant sur “wait ()” qui commenceraient tous simultanément lorsque le décompte atteindrait zéro.

     final CountDownLatch countdown = new CountDownLatch(1); for (int i = 0; i < 10; ++ i){ Thread racecar = new Thread() { public void run() { countdown.await(); //all threads waiting System.out.println("Vroom!"); } }; racecar.start(); } System.out.println("Go"); countdown.countDown(); //all threads start now! 

    Vous pouvez également utiliser ceci comme une "barrière" de style MPI qui oblige tous les threads à attendre que les autres threads se retrouvent à un certain point avant de continuer.

     final CountDownLatch countdown = new CountDownLatch(num_thread); for (int i = 0; i < num_thread; ++ i){ Thread t= new Thread() { public void run() { doSomething(); countdown.countDown(); System.out.printf("Waiting on %d other threads.",countdown.getCount()); countdown.await(); //waits until everyone reaches this point finish(); } }; t.start(); } 

    Cela dit, le loquet CountDown peut être utilisé en toute sécurité de la manière que vous avez montrée dans votre exemple.

    CountDownLatch est utilisé pour démarrer une série de threads, puis attendez qu’ils soient tous terminés (ou jusqu’à ce qu’ils appellent countDown() un nombre de fois donné).

    Le sémaphore est utilisé pour contrôler le nombre de threads simultanés qui utilisent une ressource. Cette ressource peut être quelque chose comme un fichier ou peut être le processeur en limitant le nombre de threads exécutés. Le décompte sur un sémaphore peut monter et descendre à mesure que différents threads appellent acquire() et release() .

    Dans votre exemple, vous utilisez essentiellement le sémaphore comme une sorte de compte à rebours. Étant donné que votre intention est d’attendre la fin de tous les threads, l’utilisation de CountdownLatch rend votre intention plus claire.

    Court résumé:

    1. Sémaphore et CountDownLatch ont un objective différent.

    2. Utilisez le sémaphore pour contrôler l’access des threads aux ressources.

    3. Utilisez CountDownLatch pour attendre la fin de tous les threads

    Définition de sémaphore à partir de javadocs:

    Un sémaphore maintient un ensemble de permis. Chacun acquiert () si nécessaire jusqu’à ce qu’un permis soit disponible, puis le prend. Chaque version () ajoute un permis, libérant potentiellement un acquéreur bloquant.

    Cependant, aucun object d’autorisation réel n’est utilisé; le sémaphore ne tient compte que du nombre disponible et agit en conséquence.

    Comment ça marche ?

    Les sémaphores sont utilisées pour contrôler le nombre de threads simultanés qui utilisent une ressource. Cette ressource peut être quelque chose comme une donnée partagée, un bloc de code ( section critique ) ou un fichier.

    Le décompte sur un sémaphore peut monter et descendre à mesure que différents threads appellent acquire () et release (). Mais à tout moment, vous ne pouvez pas avoir plus de threads que le nombre de sémaphores.

    Sémaphore Cas d’utilisation:

    1. Limiter l’access simultané au disque (cela peut tuer les performances en raison des recherches de disque concurrentes)
    2. Limitation de la création de thread
    3. Mise en pool / limitation de la connexion JDBC
    4. Limitation de la connexion réseau
    5. Limitation des tâches du processeur ou de la mémoire

    Jetez un oeil à cet article pour les utilisations de sémaphore.

    Définition CountDownLatch à partir de javadocs:

    Une aide à la synchronisation qui permet à un ou plusieurs threads d’attendre qu’un ensemble d’opérations soit exécuté dans d’autres threads.

    Comment ça marche?

    CountDownLatch fonctionne en ayant un compteur initialisé avec le nombre de threads, qui est décrémenté chaque fois qu’un thread termine son exécution. Lorsque le nombre atteint zéro, cela signifie que tous les threads ont terminé leur exécution et que le thread en attente de locking reprend l’exécution.

    CountDownLatch Cas d’utilisation:

    1. Atteindre un parallélisme maximal: nous souhaitons parfois lancer plusieurs threads en même temps pour obtenir un parallélisme maximal
    2. Attendre que N threads se termine avant de commencer l’exécution
    3. Détection de blocage

    Consultez cet article pour comprendre clairement les concepts CountDownLatch.

    Jetez un oeil à Fork Join Pool à cet article aussi. Il a des similitudes avec CountDownLatch .

    Disons que vous êtes entré dans un magasin de golf professionnel, en espérant trouver un quatuor,

    Lorsque vous faites la queue pour obtenir un temps de départ d’un des préposés à la boutique, essentiellement vous avez appelé proshopVendorSemaphore.acquire() , une fois que vous avez un départ, vous avez appelé proshopVendorSemaphore.release() . vous servir, c’est-à-dire une ressource partagée.

    Maintenant que vous vous approchez du démarreur, il lance un CountDownLatch(4) et appelle wait await() pour attendre les autres, pour votre part que vous avez appelé check-in, c’est-à-dire CountDownLatch . countDown() et le rest du quatuor. Quand tout arrive, le démarreur donne son accord ( await() retourne l’appel)

    Maintenant, après neuf trous, lorsque chacun de vous a fait une pause, supposons qu’il implique à nouveau le démarreur, il utilise un «nouveau» CountDownLatch(4) pour effectuer le trou 10, même attente / synchronisation que le trou 1.

    Cependant, si le starter utilisait un CyclicBarrier pour commencer, il aurait pu réinitialiser la même instance au trou 10 au lieu d’un deuxième verrou, qui utilise & jette.

    En regardant la source librement disponible, il n’y a pas de magie dans l’implémentation des deux classes, donc leurs performances devraient être sensiblement les mêmes. Choisissez celui qui rend votre intention plus évidente.

    CountdownLatch fait attendre les threads sur la méthode wait () jusqu’à ce que le compte ait atteint zéro. Alors peut-être que vous voulez que tous vos threads attendent jusqu’à 3 invocations de quelque chose, alors tous les threads peuvent aller. Un verrou ne peut généralement pas être réinitialisé.

    Un sémaphore permet aux threads d’extraire des permissions, ce qui empêche l’exécution simultanée de plusieurs threads, bloquant s’il ne peut pas obtenir le ou les permis requirejs pour continuer. Les permissions peuvent être renvoyées à un sémaphore permettant aux autres threads en attente de continuer.