CyclicBarrier v Jave

1. Úvod

Cyklické bariéry sú synchronizačné konštrukty, ktoré boli zavedené s programom Java 5 ako súčasť java.util.concurrent balíček.

V tomto článku preskúmame túto implementáciu v scenári súbežnosti.

2. Súbežnosť Java - synchronizátory

The java.util.concurrent balíček obsahuje niekoľko tried, ktoré pomáhajú spravovať množinu vlákien, ktoré navzájom spolupracujú. Niektoré z nich zahŕňajú:

  • CyclicBarrier
  • Phaser
  • CountDownLatch
  • Výmenník
  • Semafor
  • SynchronousQueue

Tieto triedy ponúkajú po vybalení funkcie pre bežné vzory interakcie medzi vláknami.

Ak máme sadu vlákien, ktoré navzájom komunikujú a pripomínajú jeden z bežných vzorov, môžeme jednoducho znova použiť príslušné triedy knižnice (nazývané tiež Synchronizátory) namiesto toho, aby ste sa pokúsili prísť s vlastnou schémou pomocou sady zámkov a stavových objektov a synchronizované kľúčové slovo.

Zamerajme sa na CyclicBarrier napredovať.

3. CyclicBarrier

A CyclicBarrier je synchronizátor, ktorý umožňuje množine vlákien čakať jeden na druhého na dosiahnutie spoločného bodu vykonania, ktorý sa tiež nazýva a bariéra.

Cyklické bariéry sa používajú v programoch, v ktorých máme pevný počet vlákien, ktoré musia pred ďalším spustením počkať na dosiahnutie spoločného bodu.

Bariéra je tzv cyklický pretože to môže byť znovu použité po uvoľnení čakajúcich vlákien.

4. Použitie

Konštruktér pre a CyclicBarrier je jednoduchý. Trvá to celé jedno číslo, ktoré označuje počet vlákien, ktoré je potrebné zavolať čakať () metóda na inštancii bariéry na označenie dosiahnutia spoločného bodu vykonania:

verejná CyclicBarrier (int párty)

Volajú sa aj vlákna, ktoré potrebujú synchronizovať svoje vykonávanie večierkov a volanie čakať () metóda je, ako môžeme zaregistrovať, že určité vlákno dosiahlo bod bariéry.

Toto volanie je synchrónne a vlákno volajúce túto metódu pozastaví vykonávanie, kým zadaný počet vlákien nevyvolá rovnakú metódu na bariére. Táto situácia vyvolala požadovaný počet vlákien čakať (), sa volá zakopnutie bariéry.

Voliteľne môžeme odovzdať druhý argument konštruktoru, ktorým je a Spustiteľné inštancia. Toto má logiku, ktorú by spustilo posledné vlákno, ktoré narazí na bariéru:

verejná CyclicBarrier (int párty, Runnable BarrierAction)

5. Implementácia

Vidieť CyclicBarrier v akcii zvážime nasledujúci scenár:

Existuje operácia, ktorá vykoná pevný počet vlákien a príslušné výsledky sa uloží do zoznamu. Keď všetky vlákna dokončia svoju činnosť, jedno z nich (zvyčajne posledné, ktoré prekoná bariéru) začne spracovávať údaje, ktoré boli načítané každým z nich.

Implementujme hlavnú triedu, kde sa dejú všetky akcie:

verejná trieda CyclicBarrierDemo {private CyclicBarrier cyklickýBariér; súkromný zoznam partialResults = Collections.synchronizedList (new ArrayList ()); private Random random = nový Random (); súkromný int NUM_PARTIAL_RESULTS; súkromný int NUM_WORKERS; // ...}

Táto trieda je celkom jednoduchá - NUM_WORKERS je počet vlákien, ktoré sa majú vykonať a NUM_PARTIAL_RESULTS je počet výsledkov, ktoré každé z pracovných vlákien vyprodukuje.

Nakoniec máme čiastočnévýsledky to je zoznam, ktorý bude ukladať výsledky každého z týchto pracovných vlákien. Upozorňujeme, že tento zoznam je Synchronizovaný zoznam pretože do nej bude súčasne písať viac vlákien a pridať () metóda nie je na vlákne bezpečná ArrayList.

Teraz poďme implementovať logiku každého z pracovných vlákien:

public class CyclicBarrierDemo {// ... class NumberCruncherThread implements Runnable {@Override public void run () {String thisThreadName = Thread.currentThread (). getName (); Zoznam partialResult = nový ArrayList (); // Zlomte niektoré čísla a uložte čiastočný výsledok pre (int i = 0; i <NUM_PARTIAL_RESULTS; i ++) {Integer num = random.nextInt (10); System.out.println (thisThreadName + ": Drvenie niektorých čísel! Konečný výsledok -" + num); partialResult.add (num); } partialResults.add (partialResult); skúsiť {System.out.println (thisThreadName + "čakať na ostatných, aby dosiahli bariéru."); cyklickýBariér.await (); } catch (InterruptedException e) {// ...} catch (BrokenBarrierException e) {// ...}}}}

Teraz implementujeme logiku, ktorá beží, keď sa bariéra spustila.

Pre zjednodušenie pridajme všetky čísla do zoznamu čiastkových výsledkov:

public class CyclicBarrierDemo {// ... class AggregatorThread implements Runnable {@Override public void run () {String thisThreadName = Thread.currentThread (). getName (); System.out.println (thisThreadName + ": Výpočetný súčet pracovníkov" + NUM_WORKERS + ", z ktorých každý má výsledky" + NUM_PARTIAL_RESULTS + ".)); int suma = 0; pre (List threadResult: partialResults) {System.out.print ("Pridávanie"); pre (Integer partialResult: threadResult) {System.out.print (partialResult + ""); suma + = čiastočnýVýsledok; } System.out.println (); } System.out.println (thisThreadName + ": Konečný výsledok =" + súčet); }}}

Posledným krokom by bola výstavba CyclicBarrier a odštartovať veci s hlavný() metóda:

public class CyclicBarrierDemo {// Predchádzajúci kód public void runSimulation (int numWorkers, int numberOfPartialResults) {NUM_PARTIAL_RESULTS = numberOfPartialResults; NUM_WORKERS = numWorkers; cyclicBarrier = nový CyclicBarrier (NUM_WORKERS, nový AggregatorThread ()); System.out.println ("Zaradenie" + NUM_WORKERS + "pracovné vlákna na výpočet" + NUM_PARTIAL_RESULTS + "čiastočné výsledky každý"); pre (int i = 0; i <NUM_WORKERS; i ++) {Pracovník vlákna = nové vlákno (nové NumberCruncherThread ()); worker.setName ("Thread" + i); worker.start (); }} public static void main (String [] args) {CyclicBarrierDemo demo = new CyclicBarrierDemo (); demo.runSimulation (5, 3); }} 

Vo vyššie uvedenom kóde sme inicializovali cyklickú bariéru s 5 vláknami, z ktorých každé produkuje 3 celé čísla ako súčasť svojho výpočtu a rovnaké ukladáme do výsledného zoznamu.

Akonáhle je bariéra spustená, posledné vlákno, ktoré bariéru spustilo, vykoná logiku uvedenú v AggregatorThread, a to - pridajte všetky čísla vyprodukované vláknami.

6. Výsledky

Tu je výstup z jednej exekúcie vyššie uvedeného programu - každá exekúcia môže vytvoriť rôzne výsledky, pretože vlákna sa dajú umiestniť v inom poradí:

Zaradenie 5 pracovných vlákien na výpočet 3 čiastkových výsledkov pre každé vlákno 0: Rozdrobenie niektorých čísel! Konečný výsledok - 6 vlákno 0: Drvenie niekoľkých čísel! Konečný výsledok - 2 vlákno 0: Drvenie niekoľkých čísel! Konečný výsledok - 2 vlákno 0 čakajúce na ostatných, aby dosiahli bariéru. Vlákno 1: Drvenie niektorých čísel! Konečný výsledok - 2 vlákno 1: Drvenie niekoľkých čísel! Konečný výsledok - 0 Vlákno 1: Drvenie niektorých čísel! Konečný výsledok - 5 vlákno 1 čakanie na ostatných, kým sa dostanú k bariére. Vlákno 3: Drvenie niektorých čísel! Konečný výsledok - 6 vlákno 3: Drvenie niekoľkých čísel! Konečný výsledok - 4 vlákno 3: Drvenie niekoľkých čísel! Konečný výsledok - 0 Vlákno 3 čakajúc na ostatných, kým sa dostanú k bariére. Vlákno 2: Drvenie niekoľkých čísel! Konečný výsledok - 1 vlákno 2: Drvenie niekoľkých čísel! Konečný výsledok - 1 vlákno 2: Drvenie niekoľkých čísel! Konečný výsledok - 0 Vlákno 2 čakajúce na ostatných, aby dosiahli bariéru. Vlákno 4: Drvenie niektorých čísel! Konečný výsledok - 9 vlákno 4: Drvenie niekoľkých čísel! Konečný výsledok - 3 vlákno 4: Drvenie niekoľkých čísel! Konečný výsledok - 5 vlákno 4 čakanie na ostatných, aby sa dostali k bariére. Závit 4: Výpočet konečného súčtu 5 pracovníkov, z ktorých každý má 3 výsledky. Pridanie 6 2 2 Pridanie 2 0 5 Pridanie 6 4 0 Pridanie 1 1 0 Pridanie 9 3 5 Vlákno 4: Konečný výsledok = 46 

Ako ukazuje vyššie uvedený výstup, Závit 4 je ten, kto prekoná bariéru a tiež vykoná logiku konečnej agregácie. Tiež nie je potrebné, aby sa vlákna spúšťali v poradí, v akom sa spúšťajú, ako ukazuje vyššie uvedený príklad.

7. Záver

V tomto článku sme videli, čo a CyclicBarrier a v akých situáciách je nápomocný.

Pred pokračovaním v ďalšej logike programu sme implementovali aj scenár, keď sme potrebovali pevný počet vlákien, aby sme dosiahli pevný bod vykonania.

Ako vždy, kód tutoriálu nájdete na GitHub.


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