Sprievodca súbežnými radmi v Jave

1. Prehľad

V tomto výučbe si prejdeme niektoré z hlavných implementácií súbežných frontov v Jave. Všeobecný úvod do front nájdete v našom Sprievodcovi Java Fronta Článok o rozhraní.

2. Fronty

V aplikáciách s viacerými vláknami musia fronty spracovávať viacero súbežných scenárov producentov a spotrebiteľov. Správna voľba súbežného radu môže byť rozhodujúca pre dosiahnutie dobrého výkonu v našich algoritmoch.

Po prvé, uvidíme niekoľko dôležitých rozdielov medzi blokujúcim a neblokovacím radom. Potom sa pozrieme na niektoré implementácie a osvedčené postupy.

2. Fronta blokovania a neblokovania

BlockingQueue ponuky jednoduchý mechanizmus bezpečný pre závit. V tomto fronte musia vlákna čakať na dostupnosť frontu. Producenti si pred pridaním prvkov počkajú na dostupnú kapacitu, zatiaľ čo zákazníci počkajú, kým sa fronta nevyprázdni. V týchto prípadoch blokovanie blokovania buď vyvolá výnimku, alebo vráti špeciálnu hodnotu, napríklad nulový alebo nepravdivé.

Na dosiahnutie tohto blokovacieho mechanizmu BlockingQueue rozhranie vystavuje nad rámec normálu dve funkcie Fronta funkcie: dať a vziať. Tieto funkcie sú ekvivalentom funkcie pridať a odstrániť v štandarde Fronta.

3. Súbežné Fronta Implementácie

3.1. ArrayBlockingQueue

Ako už názov napovedá, tento front interne využíva pole. Dôsledkom toho je ohraničený rad, čo znamená, že má pevnú veľkosť.

Jednoduchým pracovným radom je príklad použitia. Tento scenár často predstavuje nízky pomer medzi výrobcom a spotrebiteľom, kde časovo náročné úlohy rozdelíme medzi viacerých pracovníkov. Pretože tento rad nemôže rásť donekonečna, limit veľkosti slúži ako bezpečnostný prah, ak je problém s pamäťou.

Keď už hovoríme o pamäti, je dôležité poznamenať, že poradie vopred alokuje pole. Aj keď to môže zlepšiť priechodnosť, je to môže tiež spotrebovať viac pamäte, ako je potrebné. Napríklad veľkokapacitný front môže zostať dlho prázdny.

Tiež ArrayBlockingQueue používa jeden zámok pre obidve dať a vziať operácie. To zaručuje žiadne prepísanie záznamov za cenu dosiahnutého výkonu.

3.2. LinkedBlockingQueue

The LinkedBlockingQueue používa a LinkedList variant, kde je každá položka frontu novým uzlom. Aj keď to frontu v zásade robí bez obmedzenia, stále má tvrdú hranicu Celé číslo.MAX_VALUE.

Na druhej strane môžeme veľkosť frontu nastaviť pomocou konštruktora LinkedBlockingQueue (kapacita int).

Tento front používa odlišné zámky pre dať a vziať operácie. V dôsledku toho je možné obe operácie vykonať paralelne a zlepšiť tak priechodnosť.

Keďže LinkedBlockingQueue môžu byť ohraničené alebo neviazané, prečo by sme používali ArrayBlockingQueue cez tento? LinkedBlockingQueue musí alokovať a zrušiť pridelenie uzlov zakaždým, keď je položka pridaná alebo odstránená z frontu. Z tohto dôvodu, an ArrayBlockingQueue môže byť lepšou alternatívou, ak sa rad rýchlo rozrastá a rýchlo zmenšuje.

Výkonnosť LinkedBlockingQueue je vraj nepredvídateľný. Inými slovami, vždy musíme profilovať naše scenáre, aby sme zabezpečili, že používame správnu dátovú štruktúru.

3.3. Priorita blokovania

The Priorita blokovania je naše riešenie keď potrebujeme spotrebovať položky v konkrétnej objednávke. Na dosiahnutie tohto cieľa Priorita blokovania používa binárnu haldu založenú na poli.

Zatiaľ čo interne používa jediný zámkový mechanizmus, vziať prevádzka môže prebiehať súčasne s dať prevádzka. Toto umožňuje použitie jednoduchého spinlocku.

Typickým prípadom použitia je konzumácia úloh s rôznymi prioritami. Nechceme, aby úloha s nízkou prioritou nahradila úlohu s vysokou prioritou.

3.4. DelayQueue

Používame a DelayQueuekeď si spotrebiteľ môže vziať iba tovar po dobe spotreby. Je zaujímavé, že používa a PriorityQueue interne objednať položky do dátumu spotreby.

Pretože toto nie je front na všeobecné použitie, nezahŕňa toľko scenárov ako ArrayBlockingQueue alebo LinkedBlockingQueue. Napríklad môžeme tento front použiť na implementáciu jednoduchej slučky udalostí podobnej tej, ktorá sa nachádza v NodeJS. Asynchrónne úlohy umiestňujeme do frontu na ďalšie spracovanie, keď uplynie ich platnosť.

3.5. LinkedTransferQueue

The LinkedTransferQueue zavádza a prevod metóda. Zatiaľ čo iné rady sa zvyčajne blokujú pri výrobe alebo konzumácii položiek, LinkedTransferQueueumožňuje výrobcovi čakať na spotrebu položky.

Používame a LinkedTransferQueue keď potrebujeme záruku, že konkrétnu položku, ktorú sme vložili do poradia, niekto vzal. Pomocou tejto fronty tiež môžeme implementovať jednoduchý algoritmus protitlaku. Blokovaním výrobcov až do spotreby skutočne zákazníci môžu riadiť tok vytváraných správ.

3.6. SynchronousQueue

Zatiaľ čo fronty zvyčajne obsahujú veľa položiek, SynchronousQueue bude mať vždy najviac jednu položku. Inými slovami, musíme vidieť SynchronousQueue ako jednoduchý spôsob výmeny niektorých údajov medzi dvoma vláknami.

Keď máme dve vlákna, ktoré potrebujú prístup k zdieľanému stavu, často ich synchronizujeme s CountDownLatch alebo iné synchronizačné mechanizmy. Použitím a SynchronousQueue, môžeme vyhnúť sa tejto ručnej synchronizácii vlákien.

3.7. ConcurrentLinkedQueue

The ConcurrentLinkedQueue je jediný blokujúci blok tejto príručky. Preto poskytuje algoritmus „bez čakania“ pridať a anketa sú zaručene bezpečné pre vlákna a okamžite sa vrátia. Namiesto zámkov používa táto fronta CAS (Compare-And-Swap).

Interne je založený na algoritme z Jednoduché, rýchle a praktické neblokujúce a blokujúce súbežné algoritmy fronty predkladajú Maged M. Michael a Michael L. Scott.

Je to perfektný kandidát na moderné reaktívne systémy, kde je blokovanie dátových štruktúr často zakázané.

Na druhej strane, ak náš spotrebiteľ skončí v slučke, pravdepodobne by sme si mali ako lepšiu alternatívu zvoliť blokujúci rad.

4. Záver

V tejto príručke sme prešli rôznymi implementáciami súbežných radov a diskutovali o ich silných a slabých stránkach. V tejto súvislosti sme lepšie vybavení na vývoj efektívnych, odolných a dostupných systémov.


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