Vláknové modely v Jave

1. Úvod

V našich aplikáciách často musíme byť schopní robiť viac vecí súčasne. Môžeme to dosiahnuť niekoľkými spôsobmi, ale kľúčové medzi nimi je implementácia multitaskingu v nejakej podobe.

Multi-tasking znamená spustenie viacerých úloh súčasne, kde každá úloha vykonáva svoju prácu. Tieto úlohy zvyčajne prebiehajú súčasne, čítajú a zapisujú rovnakú pamäť a interagujú s rovnakými prostriedkami, ale robia rôzne veci.

2. Natívne vlákna

Štandardným spôsobom implementácie viacerých úloh v Jave je použitie vlákien. Vlákanie je zvyčajne podporované až po operačný systém. Vlákna fungujúca na tejto úrovni nazývame „natívne vlákna“.

Operačný systém má určité schopnosti s vláknami, ktoré sú pre naše aplikácie často nedostupné, jednoducho kvôli tomu, ako ďaleko je bližšie k základnému hardvéru. To znamená, že vykonávanie natívnych vlákien je zvyčajne efektívnejšie. Tieto vlákna sa priamo mapujú na vlákna vykonávania na CPU počítača - a operačný systém spravuje mapovanie vlákien na jadrá CPU.

Štandardný model vlákna v Jave, ktorý pokrýva všetky jazyky JVM, používa natívne vlákna. Toto je prípad od Java 1.2 a je to tak bez ohľadu na základný systém, na ktorom JVM beží.

To znamená, že kedykoľvek použijeme ktorýkoľvek zo štandardných mechanizmov vlákien v Jave, potom použijeme natívne vlákna. Toto zahŕňa java.lang.Thread, java.util.concurrent.Executor, java.util.concurrent.ExecutorService, a tak ďalej.

3. Zelené nite

V softvérovom inžinierstve jednou alternatívou k natívnym vláknam sú zelené vlákna. To je miesto, kde používame vlákna, ale priamo sa nemapujú na vlákna operačného systému. Namiesto toho základná architektúra spravuje samotné vlákna a spravuje, ako sa tieto mapy mapujú na vlákna operačného systému.

Spravidla to funguje spustením niekoľkých natívnych vlákien a následným pridelením zelených vlákien týmto natívnym vláknam na vykonanie. Systém potom môže zvoliť, ktoré zelené vlákna sú v danom okamihu aktívne a na ktorých natívnych vláknach sú aktívne.

To znie veľmi komplikovane a je to tak. Je to však komplikácia, o ktorú sa vo všeobecnosti nemusíme starať. O toto všetko sa stará podkladová architektúra a my ju začneme používať, akoby išlo o natívny model vlákna.

Prečo by sme to teda robili? Spustenie natívnych vlákien je veľmi efektívne, ale ich spustenie a zastavenie má vysoké náklady. Zelené vlákna pomáhajú vyhnúť sa týmto nákladom a dávajú architektúre oveľa väčšiu flexibilitu. Ak používame relatívne dlho fungujúce vlákna, potom sú natívne vlákna veľmi efektívne. V prípade veľmi krátkodobých zamestnaní môžu náklady na ich začatie prevážiť výhody ich použitia. V týchto prípadoch môžu byť zelené vlákna efektívnejšie.

Bohužiaľ Java nemá zabudovanú podporu pre zelené vlákna.

Veľmi skoré verzie používali ako štandardný model vlákien namiesto pôvodných vlákien zelené vlákna. To sa zmenilo v prostredí Java 1.2 a odvtedy na úrovni JVM neexistuje nijaká podpora.

Je tiež náročné implementovať zelené vlákna do knižníc, pretože na svoju dobrú výkonnosť by potrebovali podporu veľmi nízkej úrovne. Bežnou alternatívou ako také sú vlákna.

4. Vlákna

Vlákna sú alternatívnou formou viacerých vlákien a sú podobné zeleným vláknam. V oboch prípadoch nepoužívame natívne vlákna a namiesto nich používame základné ovládacie prvky systému, ktoré sú kedykoľvek spustené. Veľký rozdiel medzi zelenými vláknami a vláknami je v úrovni kontroly a konkrétne v tom, kto ju má pod kontrolou.

Zelené vlákna sú formou preventívneho multitaskingu. To znamená, že podkladová architektúra je úplne zodpovedná za rozhodovanie o tom, ktoré vlákna sa v danom okamihu vykonávajú.

To znamená, že platia všetky bežné problémy vlákien, pri ktorých nevieme nič o poradí vykonávania našich vlákien alebo o tom, ktoré sa budú vykonávať súčasne. Znamená to tiež, že základný systém musí byť schopný kedykoľvek pozastaviť a reštartovať náš kód, potenciálne uprostred metódy alebo dokonca vyhlásenia.

Vlákna sú namiesto toho formou kooperatívneho multitaskingu, čo znamená, že bežiace vlákno bude bežať ďalej, kým nesignalizuje, že môže ustúpiť inému. To znamená, že je našou zodpovednosťou za vzájomnú spoluprácu vlákien. Toto nám dáva priamu kontrolu nad tým, kedy môžu vlákna pozastaviť vykonávanie, namiesto toho, aby to za nás rozhodol systém.

To tiež znamená, že musíme náš kód napísať spôsobom, ktorý to umožňuje. Inak to nepôjde. Ak náš kód nemá žiadne prerušovacie body, potom by sme vôbec nemuseli používať vlákna.

Java v súčasnosti nemá zabudovanú podporu pre vlákna. Existujú niektoré knižnice, ktoré to môžu zaviesť do našich aplikácií, okrem iných aj:

4.1. Kvasar

Quasar je knižnica Java, ktorá funguje dobre s čistými programami Java a Kotlin, a má alternatívnu verziu, ktorá funguje s Clojure.

Funguje to tak, že máte agenta Java, ktorý musí bežať spolu s aplikáciou, a tento agent je zodpovedný za správu vlákien a zabezpečenie ich správnej spolupráce. Použitie agenta Java znamená, že nie sú potrebné žiadne špeciálne kroky zostavenia.

Quasar tiež vyžaduje, aby Java 11 fungovala správne, čo by mohlo obmedziť aplikácie, ktoré ju môžu používať. Staršie verzie je možné používať v prostredí Java 8, nie sú však aktívne podporované.

4.2. Kilim

Kilim je knižnica Java, ktorá ponúka veľmi podobné funkcie ako Quasar, ale robí to pomocou tkania bytecode namiesto agenta Java.. To znamená, že môže fungovať na viacerých miestach, ale komplikuje to proces zostavovania.

Kilim pracuje s jazykom Java 7 a novším a bude fungovať správne aj v scenároch, keď agent Java nie je možnosťou. Napríklad, ak sa už na prístrojové vybavenie alebo na monitorovanie používa iný.

4.3. Project Loom

Project Loom je experimentom projektu OpenJDK, ktorého cieľom je pridať vlákna do samotného JVM, a nie ako doplnkovú knižnicu. Získate tak výhody vlákien pred vláknami. Jeho priamou implementáciou do JVM môže pomôcť vyhnúť sa komplikáciám, ktoré spôsobujú agenti Java a tkanie bajtových kódov.

Pre Project Loom v súčasnosti neexistuje žiadny harmonogram vydávania, ale teraz si môžeme stiahnuť binárne súbory s prednostným prístupom, aby sme zistili, ako sa veci vyvíjajú. Pretože je však ešte veľmi skoro, musíme sa na to pri akomkoľvek produkčnom kóde spoľahnúť.

5. Rutiny

Spoločné postupy sú alternatívou k vláknam a vláknam. Co-rutiny si môžeme predstaviť ako vlákna bez akejkoľvek formy plánovania. Namiesto toho, aby základný systém rozhodoval, ktoré úlohy sa vykonávajú kedykoľvek, to náš kód robí priamo.

Spravidla píšeme spoločné rutiny tak, aby sa vynárali v konkrétnych bodoch ich toku. Môžu sa považovať za body pozastavenia v našej funkcii, kde prestane pracovať a potenciálne spôsobí nejaký medzivýsledok. Keď dosiahneme výnos, sme zastavení, kým sa volací kód z akýchkoľvek dôvodov nerozhodne znova naštartovať. To znamená, že náš volací kód riadi plánovanie, kedy bude prebiehať.

Kotlin má natívnu podporu pre spoločné rutiny zabudovanú do svojej štandardnej knižnice. Existuje niekoľko ďalších knižníc Java, ktoré môžeme použiť, ak je to žiaduce, aj na ich implementáciu.

6. Záver

V našom kóde sme videli niekoľko rôznych alternatív pre multi-tasking, od tradičných natívnych vlákien až po niektoré veľmi ľahké alternatívy. Prečo ich nevyskúšať nabudúce, keď aplikácia vyžaduje súbežnosť?


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