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.");
}
}
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.
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.
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....");
}
}
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:
- lorsque nous utilisons ExecutorService au lieu de Thread class pour créer des threads.
- 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.
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(); } } }
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
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]
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.
-
vous avez fixé le compte à 3 pour
latch
variableCountDownLatch latch = new CountDownLatch(3);
-
vous avez passé ce partage
latch
à fil Ouvrier:Processor
- trois
Runnable
les cas deProcessor
ont été soumis àExecutorService
executor
-
le fil principal (
App
) est en attente pour compter pour devenir zéro avec la déclaration ci-dessouslatch.await();
-
Processor
thread dort pendant 3 secondes puis il décrémente le comte de la valeur aveclatch.countDown()
-
première
Process
l'instance changera le nombre de verrous à 2 après son achèvement dû àlatch.countDown()
. -
deuxième
Process
l'instance changera le nombre de verrous à 1 après son achèvement dû àlatch.countDown()
. -
troisième
Process
l'instance changera le nombre de verrous à 0 une fois qu'elle sera terminée à cause delatch.countDown()
. -
Zéro compter sur le loquet de causes thread principal
App
pour sortir deawait
-
App program imprime cette sortie maintenant:
Completed
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]
meilleur exemple en temps réel pour countDownLatch expliqué dans ce lien CountDownLatchExample