Java CyclicBarrier gegen CountDownLatch

1. Einleitung

In diesem Tutorial werden wir CyclicBarrier und CountDownLatch vergleichen und versuchen, die Ähnlichkeiten und Unterschiede zwischen den beiden zu verstehen.

2. Was sind das?

Wenn es um Parallelität geht, kann es schwierig sein, sich vorzustellen, was jeder erreichen soll.

In erster Linie werden sowohl CountDownLatch als auch CyclicBarrier zum Verwalten von Multithread-Anwendungen verwendet .

Und beide sollen ausdrücken, wie ein bestimmter Thread oder eine Gruppe von Threads warten soll.

2.1. CountDownLatch

Ein CountDownLatch ist ein Konstrukt, auf das ein Thread wartet, während andere Threads auf den Latch herunterzählen, bis er Null erreicht.

Wir können uns das wie ein Gericht in einem Restaurant vorstellen, das gerade zubereitet wird. Unabhängig davon, welcher Koch so viele der n Artikel zubereitet, muss der Kellner warten, bis alle Artikel auf dem Teller sind. Wenn ein Teller n Gegenstände nimmt, zählt jeder Koch für jeden Gegenstand, den er auf den Teller legt , auf den Riegel herunter .

2.2. CyclicBarrier

Ein CyclicBarrier ist ein wiederverwendbares Konstrukt, bei dem eine Gruppe von Threads zusammen wartet, bis alle Threads eintreffen . Zu diesem Zeitpunkt ist die Barriere durchbrochen und es kann optional eine Maßnahme ergriffen werden.

Wir können uns das wie eine Gruppe von Freunden vorstellen. Jedes Mal, wenn sie in einem Restaurant essen möchten, entscheiden sie sich für einen gemeinsamen Punkt, an dem sie sich treffen können. Dort warten sie aufeinander und erst wenn alle ankommen, können sie ins Restaurant gehen, um gemeinsam zu essen.

2.3. Weiterführende Literatur

Weitere Informationen zu diesen einzelnen Themen finden Sie in unseren vorherigen Tutorials zu CountDownLatch bzw. CyclicBarrier .

3. Aufgaben gegen Themen

Lassen Sie uns einige der semantischen Unterschiede zwischen diesen beiden Klassen genauer untersuchen.

Wie in den Definitionen angegeben, können mit CyclicBarrier mehrere Threads aufeinander warten, während mit CountDownLatch ein oder mehrere Threads auf den Abschluss einer Reihe von Aufgaben warten können.

Kurz gesagt, CyclicBarrier verwaltet die Anzahl der Threads, während CountDownLatch die Anzahl der Aufgaben verwaltet .

Im folgenden Code definieren wir einen CountDownLatch mit einer Anzahl von zwei. Als nächstes rufen wir countDown () zweimal von einem einzelnen Thread aus auf:

CountDownLatch countDownLatch = new CountDownLatch(2); Thread t = new Thread(() -> { countDownLatch.countDown(); countDownLatch.countDown(); }); t.start(); countDownLatch.await(); assertEquals(0, countDownLatch.getCount());

Sobald der Latch Null erreicht, kehrt der zu wartende Anruf zurück.

Beachten Sie, dass in diesem Fall derselbe Thread die Anzahl zweimal verringern konnte.

CyclicBarrier ist in diesem Punkt jedoch anders.

Ähnlich wie im obigen Beispiel erstellen wir einen CyclicBarrier, ebenfalls mit einer Anzahl von zwei, und rufen darauf wait () auf, diesmal aus demselben Thread:

CyclicBarrier cyclicBarrier = new CyclicBarrier(2); Thread t = new Thread(() -> { try { cyclicBarrier.await(); cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { // error handling } }); t.start(); assertEquals(1, cyclicBarrier.getNumberWaiting()); assertFalse(cyclicBarrier.isBroken());

Der erste Unterschied besteht darin, dass die wartenden Fäden selbst die Barriere sind.

Zweitens, und was noch wichtiger ist, ist das zweite Warten () nutzlos . Ein einzelner Thread kann eine Barriere nicht zweimal herunterzählen .

In der Tat, denn t müssen warten , für einen anderen Thread zu nennen await () - die Zählung auf zwei zu bringen - t ‚s zweiten Anruf await () wird nicht tatsächlich geltend gemacht werden , bis die Barriere bereits gebrochen ist!

In unserem Test wurde die Barriere nicht überschritten, da nur ein Faden wartet und nicht die beiden Fäden, die für das Auslösen der Barriere erforderlich wären. Dies geht auch aus der Methode cyclicBarrier.isBroken () hervor , die false zurückgibt .

4. Wiederverwendbarkeit

Der zweitwichtigste Unterschied zwischen diesen beiden Klassen ist die Wiederverwendbarkeit. Um dies näher auszuführen, wenn die Barriere in Reisen CyclicBarrier , die Zählung wird zurückgesetzt auf den ursprünglichen Wert. CountDownLatch ist anders, da die Anzahl nie zurückgesetzt wird.

Im angegebenen Code definieren wir einen CountDownLatch mit Anzahl 7 und zählen ihn durch 20 verschiedene Aufrufe:

CountDownLatch countDownLatch = new CountDownLatch(7); ExecutorService es = Executors.newFixedThreadPool(20); for (int i = 0; i  { long prevValue = countDownLatch.getCount(); countDownLatch.countDown(); if (countDownLatch.getCount() != prevValue) { outputScraper.add("Count Updated"); } }); } es.shutdown(); assertTrue(outputScraper.size() <= 7);

Wir stellen fest, dass obwohl 20 verschiedene Threads countDown () aufrufen , die Anzahl nicht zurückgesetzt wird, sobald sie Null erreicht.

Ähnlich wie im obigen Beispiel definieren wir einen CyclicBarrier mit Anzahl 7 und warten von 20 verschiedenen Threads darauf:

CyclicBarrier cyclicBarrier = new CyclicBarrier(7); ExecutorService es = Executors.newFixedThreadPool(20); for (int i = 0; i  { try { if (cyclicBarrier.getNumberWaiting()  7);

In diesem Fall stellen wir fest, dass der Wert jedes Mal abnimmt, wenn ein neuer Thread ausgeführt wird, indem er auf den ursprünglichen Wert zurückgesetzt wird, sobald er Null erreicht.

5. Schlussfolgerung

Alles in allem CyclicBarrier und CountDownLatchsind beide hilfreiche Tools für die Synchronisation zwischen mehreren Threads. Sie unterscheiden sich jedoch grundlegend in der Funktionalität, die sie bieten. Überlegen Sie sich genau, welche für den Job geeignet ist.

Wie üblich kann auf alle besprochenen Beispiele über Github zugegriffen werden.