CyclicBarrier Java-ban

1. Bemutatkozás

Ciklikus korlátok olyan szinkronizációs konstrukciók, amelyeket a Java 5 részeként vezettek be a java.util.egyidejű csomag.

Ebben a cikkben ezt a megvalósítást egyidejűségi forgatókönyvben vizsgáljuk meg.

2. Java egyidejűség - szinkronizátorok

A java.util.egyidejű A csomag több osztályt tartalmaz, amelyek segítenek egymással együttműködő szálak kezelésében. Ezek egy része a következőket tartalmazza:

  • Ciklikus akadály
  • Phaser
  • CountDownLatch
  • Hőcserélő
  • Szemafor
  • SynchronousQueue

Ezek az osztályok a dobozon kívül kínálják a szálak közötti közös interakciós minták működését.

Ha van egy sor szálunk, amelyek kommunikálnak egymással és hasonlítanak a közös minták egyikére, egyszerűen felhasználhatjuk a megfelelő könyvtári osztályokat (más néven Szinkronizátorok) ahelyett, hogy megpróbálna létrehozni egy egyéni sémát a zárak és a feltétel objektumok halmazával és a szinkronizált kulcsszó.

Koncentráljunk a Ciklikus akadály előre menni.

3. Ciklikus akadály

A Ciklikus akadály egy szinkronizáló, amely lehetővé teszi egy szálkészlet számára, hogy megvárják, amíg eljutnak egy közös végrehajtási ponthoz, más néven a akadály.

Ciklikus korlátok olyan programokban használják, amelyekben rögzített számú szálunk van, amelyeknek a kivitelezés folytatása előtt meg kell várniuk, hogy egymás közös pontot érjenek el.

A sorompót hívják ciklikus mert a várakozó szálak elengedése után újra felhasználható.

4. Használat

A kivitelező a Ciklikus akadály egyszerű. Egyetlen egész számra van szükség, amely jelöli a szálak számát, amelyeknek meg kell hívniuk a várják() módszer a korlát példányon a közös végrehajtási pont elérését jelezni:

public CyclicBarrier (bulik)

Hívják azokat a szálakat is, amelyeknek szinkronizálniuk kell a végrehajtásukat a felek és felhívja a várják() módszerrel regisztrálhatjuk, hogy egy bizonyos szál elérte az akadálypontot.

Ez a hívás szinkron, és az ezt a módszert meghívó szál mindaddig felfüggeszti a végrehajtást, amíg egy meghatározott számú szál nem hívja ugyanazt a módszert a sorompón. Ez a helyzet, amikor a szükséges számú szál hívta meg várják(), nak, nek hívják meggátolva a korlátot.

Opcionálisan átadhatjuk a konstruktornak a második argumentumot, amely a Futható példa. Ennek logikája van, amelyet az utolsó szál futtatna, amely elhárítja a korlátot:

public CyclicBarrier (int party, Runnable barrierAction)

5. Végrehajtás

Látni Ciklikus akadály mérlegeljük a következő forgatókönyvet:

Van egy művelet, amelynek során rögzített számú szál hajtja végre és tárolja a megfelelő eredményeket egy listában. Amikor az összes szál befejezi a művelet végrehajtását, az egyikük (általában az utolsó, amely elzárja a korlátot) elkezdi feldolgozni az ezek mindegyikének beolvasott adatait.

Vezessük be azt a fő osztályt, ahol az összes művelet megtörténik:

nyilvános osztály CyclicBarrierDemo {private CyclicBarrier cyclicBarrier; privát lista részleges eredmények = gyűjtemények.synchronizedList (új ArrayList ()); private Random random = új Random (); privát int NUM_PARTIAL_RESULTS; magánintézmény NUM_WORKERS; // ...}

Ez az osztály elég egyenesen halad előre - NUM_WORKERS a végrehajtandó szálak száma és NUM_PARTIAL_RESULTS a munkaszálak által elért eredmények száma.

Végül megvan részeredmények ezek egy lista, amely tárolja ezen munkaszálak eredményeit. Ne feledje, hogy ez a lista a SynchronizedList mert egyszerre több szál fog írni hozzá, és a add () módszer nem szálkamentes egy sima felületen Tömb lista.

Most valósítsuk meg az egyes munkaszálak logikáját:

public class CyclicBarrierDemo {// ... class NumberCruncherThread Runnable {@Override public void run () {String thisThreadName = Thread.currentThread (). getName (); List parciális eredmény = new ArrayList (); // Vágjon össze néhány számot, és tárolja a részeredményt (int i = 0; i <NUM_PARTIAL_RESULTS; i ++) {Egész szám = véletlen.nextInt (10); System.out.println (thisThreadName + ": Egyes számok összeomlása! Végeredmény -" + szám); részeredmény.add (szám); } részlegesEredmények.add (részlegesEredmények); próbáld meg a {System.out.println (thisThreadName + "várakozás, hogy mások elérjék a korlátot"); cyclicBarrier.await (); } catch (InterruptedException e) {// ...} catch (BrokenBarrierException e) {// ...}}}}

Most végre fogjuk hajtani azt a logikát, amely akkor működik, amikor a sorompó ki van oldva.

A dolgok egyszerűsége érdekében adjuk hozzá az összes számot a részeredménylistában:

public class CyclicBarrierDemo {// ... class AggregatorThread Runnable {@Override public void run () {String thisThreadName = Thread.currentThread (). getName (); System.out.println (thisThreadName + ":" + NUM_WORKERS + "munkavállaló összegének kiszámítása," + NUM_PARTIAL_RESULTS + "eredményt kapva."); int összeg = 0; for (Lista szál eredménye: részleges eredmény) {System.out.print ("Hozzáadás"); for (Egész szám részeredmény: szál eredmény) {System.out.print (részleges eredmény + ""); összeg + = részeredmény; } System.out.println (); } System.out.println (thisThreadName + ": Végeredmény =" + összeg); }}}

Az utolsó lépés a Ciklikus akadály és rúgni a dolgokat egy fő() módszer:

public class CyclicBarrierDemo {// Előző kód public void runSimulation (int numWorkers, int numberOfPartialResults) {NUM_PARTIAL_RESULTS = numberOfPartialResults; NUM_WORKERS = számmunkások; cyclicBarrier = új CyclicBarrier (NUM_WORKERS, új AggregatorThread ()); System.out.println ("Spawning" + NUM_WORKERS + "dolgozó szálak a" + NUM_PARTIAL_RESULTS + "mindegyik részeredmény" kiszámításához); for (int i = 0; i <NUM_WORKERS; i ++) {Thread worker = new Thread (új NumberCruncherThread ()); worker.setName ("Menet" + i); munkás.indítás (); }} public static void main (String [] érvel) {CyclicBarrierDemo demo = new CyclicBarrierDemo (); demo.runSimulation (5, 3); }} 

A fenti kódban 5 szálral inicializáltuk a ciklikus akadályt, amelyek mindegyike 3 egész számot eredményez a számítás részeként, és ugyanezt tárolja a kapott listában.

Miután a korlát megszakadt, az utolsó szál, amely megszakította a korlátot, végrehajtja az AggregatorThread-ben megadott logikát, nevezetesen - adja hozzá a szálak által előállított összes számot.

6. Eredmények

Itt található a fenti program egy végrehajtásának kimenete - minden egyes végrehajtás eltérő eredményeket hozhat létre, mivel a szálak más sorrendben hozhatók létre:

5 munkásszál ívása 3 részeredmény kiszámításához 0-as szál: Néhány szám összezúzása! Végeredmény - 6 szál 0: Összevissza néhány szám! Végeredmény - 2 szál 0: Összevissza néhány szám! Végeredmény - 2 0 szál várja, hogy mások elérjék a korlátot. 1. szál: Néhány szám összezúzása! Végeredmény - 2 1. szál: Összevissza néhány szám! Végeredmény - 0 1. szál: Néhány szám összeomlik! Végeredmény - 5 1. szál várja, hogy mások elérjék a korlátot. 3. szál: Néhány szám összezúzása! Végeredmény - 6 3. szál: Néhány szám roppantása! Végeredmény - 4 3. szál: Összevissza néhány szám! Végeredmény - 0 3. szál várja, hogy mások elérjék az akadályt. 2. szál: Néhány szám összezúzása! Végeredmény - 1 2. szál: Néhány szám roppantása! Végeredmény - 1 2. szál: Néhány szám roppantása! Végeredmény - 0 2. szál arra vár, hogy mások elérjék az akadályt. 4. szál: Néhány szám összezúzása! Végeredmény - 9 4. szál: Néhány szám roppantása! Végeredmény - 3 4. szál: Összevissza néhány szám! Végeredmény - 5 4. szál várja, hogy mások elérjék az akadályt. 4. szál: 5 dolgozó végső összegének kiszámítása, mindegyiknek 3 eredménye. Összeadás 6 2 2 Összeadás 2 0 5 Összeadás 6 4 0 Összeadás 1 1 0 Összeadás 9 3 5 4. szál: Végeredmény = 46 

Amint a fenti kimenet mutatja, 4. szál az, amely kioldja a korlátot, és végrehajtja a végső összesítési logikát is. Az sem szükséges, hogy a szálak ténylegesen abban a sorrendben fussanak, ahogyan a fenti példa mutatja.

7. Következtetés

Ebben a cikkben azt láttuk, hogy mi a Ciklikus akadály és milyen helyzetekben hasznos.

Olyan forgatókönyvet is megvalósítottunk, ahol rögzített számú szálra volt szükségünk egy fix végrehajtási pont eléréséhez, mielőtt folytatnánk a többi programlogikát.

Mint mindig, az oktatóanyag kódja megtalálható a GitHubon.