CyclicBarrier i Java

1. Introduksjon

Sykliske barrierer er synkroniseringskonstruksjoner som ble introdusert med Java 5 som en del av java.util.concurrent pakke.

I denne artikkelen vil vi utforske denne implementeringen i et samtidighetsscenario.

2. Java samtidighet - synkroniserere

De java.util.concurrent pakken inneholder flere klasser som hjelper deg med å administrere et sett med tråder som samarbeider med hverandre. Noen av disse inkluderer:

  • CyclicBarrier
  • Phaser
  • CountDownLatch
  • Veksler
  • Semafor
  • SynchronousQueue

Disse klassene tilbyr funksjonalitet utenom boksen for vanlige interaksjonsmønstre mellom tråder.

Hvis vi har et sett med tråder som kommuniserer med hverandre og ligner på et av de vanlige mønstrene, vi kan rett og slett bruke de aktuelle bibliotekstimene (også kalt Synkronisering) i stedet for å prøve å komme opp med et tilpasset oppsett ved hjelp av et sett med låser og tilstandsobjekter og synkronisert nøkkelord.

La oss fokusere på CyclicBarrier fremover.

3. CyclicBarrier

EN CyclicBarrier er en synkroniser som lar et sett med tråder vente på at hverandre skal nå et felles utførelsespunkt, også kalt a barriere.

Sykliske barrierer brukes i programmer der vi har et fast antall tråder som må vente på at hverandre skal nå et felles punkt før vi fortsetter kjøringen.

Barrieren kalles syklisk fordi den kan brukes på nytt etter at ventetrådene er utgitt.

4. Bruk

Konstruktøren for en CyclicBarrier er enkelt. Det tar et enkelt heltall som angir antall tråder som trenger å ringe avvente() metode på barriereforekomsten for å betegne at det felles utførelsespunktet:

offentlig CyclicBarrier (int parter)

Trådene som trenger å synkronisere utførelsen kalles også fester og kaller avvente() metoden er hvordan vi kan registrere at en bestemt tråd har nådd sperrepunktet.

Denne samtalen er synkron, og tråden som kaller denne metoden stanser utførelsen til et spesifisert antall tråder har kalt den samme metoden på barrieren. Denne situasjonen hvor det nødvendige antall tråder har ringt avvente(), er kalt tripping barrieren.

Eventuelt kan vi overføre det andre argumentet til konstruktøren, som er a Kjørbar forekomst. Dette har logikk som vil bli drevet av den siste tråden som utløser barrieren:

public CyclicBarrier (int parties, Runnable barrierAction)

5. Implementering

Å se CyclicBarrier i aksjon, la oss vurdere følgende scenario:

Det er en operasjon som et fast antall tråder utfører og lagrer de tilsvarende resultatene i en liste. Når alle tråder er ferdige med å utføre handlingen, begynner en av dem (vanligvis den siste som utløser barrieren) å behandle dataene som ble hentet av hver av disse.

La oss implementere hovedklassen der all handlingen skjer:

offentlig klasse CyclicBarrierDemo {private CyclicBarrier cyclicBarrier; privat liste partialResults = Collections.synchronizedList (ny ArrayList ()); private Tilfeldig tilfeldig = ny Tilfeldig (); privat int NUM_PARTIAL_RESULTS; privat int NUM_WORKERS; // ...}

Denne klassen er ganske rett frem - NUM_WORKERS er antall tråder som skal kjøres og NUM_PARTIAL_RESULTS er antall resultater som hver av arbeidertrådene skal produsere.

Endelig har vi det delvisResultater som er en liste som skal lagre resultatene av hver av disse arbeidstakertrådene. Vær oppmerksom på at denne listen er en SynchronizedList fordi flere tråder vil skrive til den samtidig, og legge til() metoden er ikke trådsikker på en vanlig ArrayList.

La oss nå implementere logikken til hver av arbeidertrådene:

offentlig klasse CyclicBarrierDemo {// ... klasse NumberCruncherThread implementerer Runnable {@Override public void run () {String thisThreadName = Thread.currentThread (). getName (); Liste partialResult = ny ArrayList (); // Knusing noen tall og lagre delresultatet for (int i = 0; i <NUM_PARTIAL_RESULTS; i ++) {Heltall num = random.nextInt (10); System.out.println (thisThreadName + ": Knusing av noen tall! Sluttresultat -" + num); partialResult.add (num); } partialResults.add (partialResult); prøv {System.out.println (thisThreadName + "venter på at andre skal nå barrieren."); cyclicBarrier.await (); } fangst (InterruptedException e) {// ...} catch (BrokenBarrierException e) {// ...}}}}

Vi implementerer nå logikken som går når barrieren er utløst.

For å gjøre ting enkelt, la oss bare legge til alle tallene i listen over delresultater:

offentlig klasse CyclicBarrierDemo {// ... klasse AggregatorThread implementerer Runnable {@Override public void run () {String thisThreadName = Thread.currentThread (). getName (); System.out.println (thisThreadName + ": Beregningssummen av" + NUM_WORKERS + "arbeidere, med" + NUM_PARTIAL_RESULTS + "resultater hver."); int sum = 0; for (List threadResult: partialResults) {System.out.print ("Legger til"); for (Integer partialResult: threadResult) {System.out.print (partialResult + ""); sum + = partialResult; } System.out.println (); } System.out.println (thisThreadName + ": Endelig resultat =" + sum); }}}

Det siste trinnet ville være å konstruere CyclicBarrier og sparke ting med en hoved() metode:

offentlig klasse CyclicBarrierDemo {// Forrige kode public void runSimulation (int numWorkers, int numberOfPartialResults) {NUM_PARTIAL_RESULTS = numberOfPartialResults; NUM_WORKERS = antall arbeidere; cyclicBarrier = new CyclicBarrier (NUM_WORKERS, new AggregatorThread ()); System.out.println ("Gyting" + NUM_WORKERS + "arbeidertråder for å beregne" + NUM_PARTIAL_RESULTS + "delvise resultater hver"); for (int i = 0; i <NUM_WORKERS; i ++) {Trådarbeider = ny tråd (ny NumberCruncherThread ()); worker.setName ("tråd" + i); worker.start (); }} public static void main (String [] args) {CyclicBarrierDemo demo = new CyclicBarrierDemo (); demo.runSimulation (5, 3); }} 

I koden ovenfor initialiserte vi den sykliske barrieren med 5 tråder som hver produserer 3 heltall som en del av beregningen og lagrer det samme i den resulterende listen.

Når barrieren er utløst, utfører den siste tråden som utløste barrieren logikken som er spesifisert i AggregatorThread, nemlig - legg til alle tallene som produseres av trådene.

6. Resultater

Her er utdataene fra en utførelse av programmet ovenfor - hver utførelse kan skape forskjellige resultater ettersom trådene kan gytes i en annen rekkefølge:

Gyter 5 arbeidertråder for å beregne 3 delresultater hver Tråd 0: Knusing noen tall! Sluttresultat - 6 Tråd 0: Knusing noen tall! Sluttresultat - 2 Tråd 0: Knusing noen tall! Sluttresultat - 2 tråd 0 venter på at andre skal nå barrieren. Tråd 1: Knusing noen tall! Sluttresultat - 2 Tråd 1: Knusing noen tall! Sluttresultat - 0 Tråd 1: Knusing noen tall! Sluttresultat - 5 tråd 1 venter på at andre skal nå barrieren. Tråd 3: Knusing noen tall! Sluttresultat - 6 Tråd 3: Knusing noen tall! Sluttresultat - 4 Tråd 3: Knusing noen tall! Sluttresultat - 0 Tråd 3 venter på at andre skal nå barrieren. Tråd 2: Knusing noen tall! Sluttresultat - 1 Tråd 2: Knusing noen tall! Sluttresultat - 1 Tråd 2: Knusing noen tall! Sluttresultat - 0 Tråd 2 venter på at andre skal nå barrieren. Tråd 4: Knusing noen tall! Sluttresultat - 9 Tråd 4: Knusing noen tall! Sluttresultat - 3 Tråd 4: Knusing noen tall! Sluttresultat - 5 tråd 4 som venter på at andre skal nå barrieren. Tråd 4: Beregner den endelige summen av 5 arbeidere, med tre resultater hver. Legge til 6 2 2 Legge til 2 0 5 Legge til 6 4 0 Legge til 1 1 0 Legge til 9 3 5 Tråd 4: Sluttresultat = 46 

Som ovenstående viser, Tråd 4 er den som utløser barrieren og også utfører den endelige aggregeringslogikken. Det er heller ikke nødvendig at tråder faktisk kjøres i den rekkefølgen de startes som eksemplet ovenfor viser.

7. Konklusjon

I denne artikkelen så vi hva en CyclicBarrier er, og hva slags situasjoner det er nyttig i.

Vi implementerte også et scenario der vi trengte et fast antall tråder for å nå et fast utførelsespunkt før vi fortsatte med annen programlogikk.

Som alltid kan koden for opplæringen bli funnet på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found