Modeli navoja u Javi

1. Uvod

Često u našim aplikacijama moramo biti u mogućnosti raditi više stvari istovremeno. To možemo postići na nekoliko načina, ali ključni među njima je implementacija multitaskinga u nekom obliku.

Više zadataka znači istodobno pokretanje više zadataka, gdje svaki zadatak obavlja svoj posao. Ti se zadaci obično izvode istovremeno, čitajući i zapisujući istu memoriju i komunicirajući s istim resursima, ali radeći različite stvari.

2. Izvorne niti

Standardni način implementacije višezadaćnosti u Javi je upotreba niti. Threading je obično podržan do operativnog sustava. Niti koje rade na ovoj razini nazivamo "izvornim nitima".

Operativni sustav ima neke mogućnosti uvođenja niti, koje su često nedostupne našim aplikacijama, jednostavno zbog toga što je bliže osnovnom hardveru. To znači da su izvršavanje nativnih niti obično učinkovitije. Te se niti izravno preslikavaju na niti izvršenja na računalu CPU - a operativni sustav upravlja mapiranjem niti na jezgre procesora.

Standardni model navoja navoja u Javi, koji pokriva sve JVM jezike, koristi izvorne niti. To je slučaj od Jave 1.2 i slučaj je bez obzira na temeljni sustav na kojem je JVM pokrenut.

To znači da svaki put kada koristimo bilo koji od standardnih mehanizama za rezanje navoja u Javi, tada koristimo izvorne niti. Ovo uključuje java.lang.Nit, java.util.concurrent.Izvršitelj, java.util.concurrent.ExecutorService, i tako dalje.

3. Zelene niti

U softverskom inženjerstvu, jedna od alternativa izvornim nitima su zelene niti. Ovdje koristimo niti, ali one se ne mapiraju izravno na niti operativnog sustava. Umjesto toga, temeljna arhitektura sama upravlja nitima i upravlja načinom na koji se te mapiraju na niti operativnog sustava.

To obično radi pokretanjem nekoliko izvornih niti, a zatim dodjeljivanjem zelenih niti na te izvorne niti za izvršenje. Tada sustav može odabrati koje su zelene niti aktivne u bilo kojem trenutku i na kojim su izvornim nitima aktivne.

Ovo zvuči vrlo komplicirano, i jest. Ali to je komplikacija zbog koje općenito ne trebamo mariti. Osnovna arhitektura brine se o svemu tome, a mi je koristimo kao da je to izvorni model navoja.

Pa zašto bismo to činili? Izvorne niti vrlo su učinkovite za pokretanje, ali imaju visoku cijenu oko pokretanja i zaustavljanja. Zelene niti pomažu izbjeći ovaj trošak i daju arhitekturi puno veću fleksibilnost. Ako koristimo relativno dugotrajne niti, tada su izvorne niti vrlo učinkovite. Za vrlo kratkotrajne poslove, troškovi njihovog započinjanja mogu nadići korist od njihovog korištenja. U tim slučajevima zelene niti mogu postati učinkovitije.

Nažalost, Java nema ugrađenu podršku za zelene niti.

Vrlo rane verzije koristile su zelene niti umjesto izvornih niti kao standardni model navoja. To se promijenilo u Javi 1.2 i od tada nije bilo podrške za to na razini JVM.

Također je izazov implementirati zelene niti u knjižnice jer bi im za vrlo dobru izvedbu trebala podrška na vrlo niskoj razini. Kao takva, uobičajena alternativa koja se koristi su vlakna.

4. Vlakna

Vlakna su alternativni oblik višestrukog navoja i slična su zelenim nitima. U oba slučaja ne koristimo izvorne niti, već koristimo osnovne sistemske kontrole koje se izvode u bilo kojem trenutku. Velika razlika između zelenih niti i vlakana je u razini kontrole i konkretno tko kontrolira.

Zelene niti oblik su preventivnog multitaskinga. To znači da je temeljna arhitektura u potpunosti odgovorna za odlučivanje koje se niti izvršavaju u bilo kojem trenutku.

To znači da se primjenjuju svi uobičajeni problemi s navojem niti, gdje ne znamo ništa o redoslijedu izvršenja naših niti ili koje će se izvršavati istodobno. To također znači da temeljni sustav mora moći pauzirati i ponovno pokrenuti naš kôd u bilo kojem trenutku, potencijalno usred metode ili čak izjave.

Vlakna su umjesto toga oblik suradničkog multitaskinga, što znači da će se izvodljiva nit nastaviti izvoditi dok ne signalizira da može ustupiti drugom. To znači da je naša odgovornost da vlakna međusobno surađuju. To nam stavlja izravnu kontrolu nad time kada vlakna mogu zaustaviti izvršenje, umjesto da sustav odlučuje o nama.

To također znači da svoj kod moramo napisati na način koji to omogućava. Inače neće uspjeti. Ako naš kôd nema točaka prekida, možda uopće ne koristimo vlakna.

Java trenutno nema ugrađenu podršku za vlakna. Postoje neke knjižnice koje to mogu uvesti u naše aplikacije, uključujući, ali ne ograničavajući se na:

4.1. Kvazar

Quasar je Java knjižnica koja dobro radi s čistom Javom i Kotlinom i ima alternativnu verziju koja radi s Clojureom.

Djeluje tako da ima Java agent koji se treba izvoditi uz aplikaciju, a taj je agent odgovoran za upravljanje vlaknima i osiguravanje ispravne suradnje. Korištenje Java agenta znači da nisu potrebni posebni koraci izrade.

Quasar također zahtijeva da Java 11 radi ispravno, što može ograničiti programe koji ga mogu koristiti. Na Java 8 mogu se koristiti starije verzije, ali one nisu aktivno podržane.

4.2. Kilim

Kilim je Java knjižnica koja nudi vrlo slične funkcije kao i Quasar, ali to čini koristeći tkanje bajt-koda umjesto Java agenta. To znači da može raditi na više mjesta, ali postupak gradnje čini složenijim.

Kilim radi s Javom 7 i novijom i ispravno će raditi čak i u scenarijima kada Java agent nije opcija. Na primjer, ako se drugi koristi već za instrumentaciju ili nadzor.

4.3. Projekt razboj

Project Loom eksperiment je projekta OpenJDK za dodavanje vlakana u sam JVM, umjesto kao dodatak u biblioteku. To će nam dati prednost vlakana u odnosu na niti. Primjenom izravno na JVM može pomoći u izbjegavanju komplikacija koje Java agenti i tkanje bajt-koda uvode.

Trenutno ne postoji raspored izdavanja Project Loom-a, ali trenutno možemo preuzeti binarne datoteke s pristupom ranom pristupu kako bismo vidjeli kako stoje stvari. Međutim, budući da je još vrlo rano, moramo biti oprezni oslanjajući se na to za bilo koji proizvodni kod.

5. Su-rutine

Ko-rutine su alternativa navoju i vlaknima. Ko-rutine možemo smatrati vlaknima bez ikakvog oblika zakazivanja. Umjesto da temeljni sustav odlučuje koje zadatke u bilo kojem trenutku izvršava, naš kod to čini izravno.

Općenito, ko-rutine pišemo tako da se daju u određenim točkama svog toka. To se mogu vidjeti kao točke pauze u našoj funkciji, gdje će prestati raditi i potencijalno rezultirati nekim srednjim rezultatom. Kad popustimo, zaustavljamo se sve dok nas pozivni broj iz bilo kojeg razloga ne odluči ponovno pokrenuti. To znači da naš pozivni kod kontrolira raspoređivanje kada će se to izvesti.

Kotlin ima matičnu podršku za ko-rutine ugrađenu u svoju standardnu ​​knjižnicu. Postoji nekoliko drugih Java knjižnica koje možemo koristiti i za njihovu primjenu po želji.

6. Zaključak

U našem kodu vidjeli smo nekoliko različitih mogućnosti za višestruke zadatke, od tradicionalnih izvornih niti do vrlo laganih alternativa. Zašto ih ne isprobati sljedeći put kad aplikaciji treba istodobnost?