Java CyclicBarrier versus CountDownLatch

1. Inleiding

In deze tutorial zullen we het vergelijken CyclicBarrier en CountDownLatch en probeer de overeenkomsten en verschillen tussen de twee te begrijpen.

2. Wat zijn dit?

Als het op gelijktijdigheid aankomt, kan het een uitdaging zijn om te bedenken wat elk bedoeld is om te bereiken.

In de eerste plaats beide CountDownLatch en CyclicBarrier worden gebruikt voor het beheren van multi-threaded applicaties.

En, ze zijn beide bedoeld om uit te drukken hoe een bepaalde thread of groep threads moet wachten.

2.1. CountDownLatch

EEN CountDownLatch is een constructie die een draad is wachts aan terwijl andere threads aftellen op de grendel totdat deze nul bereikt.

We kunnen dit zien als een gerecht in een restaurant dat in voorbereiding is. Welke kok ook bereidt, maar veel van de n items, moet de ober wacht totdat alle items op het bord liggen. Als een bord pakt n items, zal elke kok aftellen op de grendel voor elk item dat ze op het bord legt.

2.2. CyclicBarrier

EEN CyclicBarrier is een herbruikbare constructie waar een groep draden wacht samen tot alle draden aankomen. Op dat moment is de barrière doorbroken en een actie kan optioneel worden genomen.

We kunnen dit zien als een groep vrienden. Elke keer dat ze van plan zijn om in een restaurant te eten, beslissen ze een gemeenschappelijk punt waar ze elkaar kunnen ontmoeten. Ze wacht voor elkaar daar, en alleen als iedereen komt aan kunnen ze naar het restaurant gaan om samen te eten.

2.3. Verder lezen

En voor veel meer details over elk van deze afzonderlijk, zie onze vorige tutorials over CountDownLatch en CyclicBarrier respectievelijk.

3. Taken versus discussielijnen

Laten we dieper ingaan op enkele van de semantische verschillen tussen deze twee klassen.

Zoals vermeld in de definities, CyclicBarrier laat een aantal threads op elkaar wachten, terwijl CountDownLatch laat een of meer threads wachten totdat een aantal taken is voltooid.

Kortom, CyclicBarrier houdt een telling bij van draden terwijl CountDownLatch houdt een telling bij van taken.

In de volgende code definiëren we een CountDownLatch met een telling van twee. Vervolgens bellen we countDown () tweemaal van een enkele thread:

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

Zodra de vergrendeling nul bereikt, wordt de oproep naar wachten geeft terug.

Merk op dat in dit geval we waren in staat om dezelfde thread het aantal twee keer te laten verminderen.

CyclicBarrier, op dit punt is het echter anders.

Net als in het bovenstaande voorbeeld maken we een CyclicBarrier, opnieuw met een telling van twee en call wachten() erop, dit keer van dezelfde draad:

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

Het eerste verschil hier is dat de draden die wachten, zelf de barrière zijn.

Ten tweede, en nog belangrijker, de seconde wachten() is nutteloos. Een enkele draad kan dat niet aftellen twee keer een barrière.

Inderdaad, omdat t moet wacht voor een andere thread om te bellen wachten() - om de telling op twee te brengen - t‘S tweede oproep aan wachten() zal niet echt worden aangeroepen totdat de barrière al is doorbroken!

In onze test de barrière is niet gepasseerd omdat er maar één draad wacht en niet de twee draden die nodig zouden zijn om de barrière te doorbreken. Dit blijkt ook uit de cyclicBarrier.isBroken () methode, die terugkeert false.

4. Herbruikbaarheid

Het tweede meest opvallende verschil tussen deze twee klassen is herbruikbaarheid. Uitbreiden, wanneer de slagboom binnendringt CyclicBarrier, wordt de telling teruggezet naar de oorspronkelijke waarde.CountDownLatch is anders omdat de telling nooit wordt gereset.

In de gegeven code definiëren we een CountDownLatch met tel 7 en tel het tot 20 verschillende oproepen:

CountDownLatch countDownLatch = nieuwe 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);

We zien dat ook al bellen 20 verschillende threads countDown (), wordt de telling niet gereset zodra deze nul bereikt.

Net als in het bovenstaande voorbeeld definiëren we een CyclicBarrier met tel 7 en wacht erop van 20 verschillende threads:

CyclicBarrier cyclicBarrier = nieuwe CyclicBarrier (7); ExecutorService es = Executors.newFixedThreadPool (20); voor (int i = 0; ik {probeer {if (cyclicBarrier.getNumberWaiting () 7);

In dit geval zien we dat de waarde afneemt elke keer dat een nieuwe thread wordt uitgevoerd, door te resetten naar de oorspronkelijke waarde zodra deze nul bereikt.

5. Conclusie

Globaal genomen, CyclicBarrier en CountDownLatchzijn beide handige tools voor synchronisatie tussen meerdere threads. Ze verschillen echter fundamenteel in termen van de functionaliteit die ze bieden. Overweeg elk zorgvuldig om te bepalen welke geschikt is voor de taak.

Zoals gewoonlijk zijn alle besproken voorbeelden toegankelijk via Github.