Razlika između niti i virtualne niti u Javi

1. Uvod

U ovom uputstvu pokazat ćemo razliku između tradicionalnih niti u Javi i virtualnih niti uvedenih u Project Loom.

Dalje ćemo podijeliti nekoliko slučajeva uporabe za virtualne niti i API-je koje je projekt uveo.

Prije nego što započnemo, moramo napomenuti ovaj se projekt aktivno razvija. Pokretat ćemo naše primjere na VM razboju za rani pristup: openjdk-15-loom + 4-55_windows-x64_bin.

Novije verzije gradnji mogu slobodno mijenjati i razbijati trenutne API-je. To je rečeno, već je došlo do velike promjene u API-u, kao i ranije korišten java.lang.Fiber razred je uklonjen i zamijenjen novim java.lang.VirtualThread razred.

2. Pregled navoja na visokoj razini naspram virtualne niti

Na visokoj razini, niti upravlja i planira operativni sustav, dok virtualnom niti upravlja i planira virtualni stroj. Sada, da bismo stvorili novu nit jezgre, moramo obaviti sistemski poziv, a to je skupa operacija.

Zbog toga koristimo spremišta niti umjesto preraspodjele i uklanjanja niti po potrebi. Dalje, ako želimo prilagoditi svoju aplikaciju dodavanjem dodatnih niti, zbog prebacivanja konteksta i njihovog memorijskog otiska, troškovi održavanja tih niti mogu biti značajni i utjecati na vrijeme obrade.

Tada obično ne želimo blokirati te niti, a to rezultira upotrebom neblokirajućih I / O API-ja i asinkronih API-ja, što bi moglo zatrpati naš kôd.

Baš suprotno, virtualnim nitima upravlja JVM. Stoga, njihov za dodjelu nije potreban sistemski poziv, i oni su bez prekidača konteksta operacijskog sustava. Nadalje, virtualne niti rade na niti nositelja, što je stvarna nit jezgre koja se koristi ispod haube. Kao rezultat toga, budući da smo oslobođeni preklopnika konteksta sustava, mogli bismo stvoriti još mnogo takvih virtualnih niti.

Dalje, ključno svojstvo virtualnih niti je da ne blokiraju našu nit nositelja. Uz to, blokiranje virtualne niti postaje mnogo jeftinija operacija, jer će JVM zakazati još jednu virtualnu nit, ostavljajući nit nositelja deblokiranom.

U konačnici, ne bismo trebali posezati za NIO ili Async API-ima. To bi trebalo rezultirati čitljivijim kodom koji je lakši za razumijevanje i uklanjanje pogrešaka. Štoviše, nastavak može potencijalno blokirati nit nosača - konkretno, kada nit poziva nativnu metodu i odatle izvodi operacije blokiranja.

3. Novi API graditelja niti

U Loomu smo u API-ju dobili novi API graditelja Nit klase, zajedno s nekoliko tvorničkih metoda. Pogledajmo kako možemo stvoriti standardne i virtualne tvornice i iskoristiti ih za izvršavanje niti:

Izvodljivi printThread = () -> System.out.println (Thread.currentThread ()); ThreadFactory virtualThreadFactory = Thread.builder (). Virtual (). Factory (); ThreadFactory kernelThreadFactory = Thread.builder (). Factory (); Tema virtualThread = virtualThreadFactory.newThread (printThread); Nit kernelThread = kernelThreadFactory.newThread (printThread); virtualThread.start (); kernelThread.start ();

Evo rezultata gore navedenog izvođenja:

Tema [Thread-0,5, glavna] VirtualThread [, ForkJoinPool-1-worker-3, CarrierThreads]

Ovdje je prvi unos standard toString izlaz niti jezgre.

Sada u izlazu vidimo da virtualna nit nema ime i izvršava se na radnoj niti spremišta Fork-Join iz CarrierThreads grupa niti.

Kao što možemo vidjeti, bez obzira na temeljnu provedbu, API je isti, a to znači da bismo lako mogli pokrenuti postojeći kod na virtualnim nitima.

Također, ne trebamo naučiti novi API da bismo ih iskoristili.

4. Sastav virtualne niti

To je nastavak i rokovnik koji zajedno čine virtualnu nit. Sada naš planiratelj korisničkog načina može biti bilo koja implementacija Izvršitelj sučelje. Gornji primjer pokazao nam je da po defaultu radimo na ForkJoinPool.

Sada je, slično niti jezgre - koja se može izvršiti na CPU-u, zatim parkirati, preurediti natrag i zatim nastaviti svoje izvršavanje - nastavak je izvršna jedinica koja se može pokrenuti, zatim parkirati (ustupiti), ponovno rasporediti i nastaviti njegovo izvršavanje na isti način odakle je stao i njime će i dalje upravljati JVM umjesto da se oslanja na operativni sustav.

Imajte na umu da je nastavak API niske razine i da bi programeri trebali koristiti API-je više razine poput API-ja graditelja za pokretanje virtualnih niti.

Međutim, kako bismo pokazali kako to radi ispod haube, sada ćemo pokrenuti naš eksperimentalni nastavak:

var doseg = novi ContinuationScope ("C1"); var c = novo Nastavak (opseg, () -> {System.out.println ("Start C1"); Continuation.yield (opseg); System.out.println ("Kraj C1");}); while (! c.isDone ()) {System.out.println ("Start run ()"); c.run (); System.out.println ("Završi pokretanje ()"); }

Evo rezultata gore navedenog izvođenja:

Start run () Start C1 End run () Start run () End C1 End run ()

U ovom smo primjeru nastavili i u jednom smo trenutku odlučili zaustaviti obradu. Zatim, nakon što smo ga ponovno pokrenuli, naš se nastavak nastavio tamo gdje je stao. Po izlazu vidimo da trčanje() metoda je pozvana dva puta, ali nastavak je započet jednom, a zatim je nastavio svoje izvršavanje u drugoj vožnji odakle je stao.

Na ovaj način JVM treba obraditi operacije blokiranja. Jednom kada se dogodi operacija blokiranja, nastavak će rezultirati, ostavljajući nit nosača deblokiranom.

Dakle, dogodilo se to što je naša glavna nit stvorila novi okvir steka na svom stogu poziva za trčanje() metodom i nastavio s izvršenjem. Zatim, nakon što je nastavak popustio, JVM je spasio trenutno stanje izvršenja.

Dalje, glavna nit je nastavila svoje izvršavanje kao da je trčanje() metoda vraćena i nastavljena s dok petlja. Nakon drugog poziva na nastavak trčanje metodom, JVM je vratio stanje glavne niti do točke kada je nastavak donio i dovršio izvršenje.

5. Zaključak

U ovom smo članku razgovarali o razlici između niti jezgre i virtualne niti. Dalje, pokazali smo kako bismo mogli koristiti novi API graditelja niti od Project Loom za pokretanje virtualnih niti.

Napokon smo pokazali što je nastavak i kako to djeluje ispod haube. Možemo dalje istraživati ​​stanje Project Looma pregledavanjem VM-a s ranim pristupom. Alternativno, možemo istražiti više već standardiziranih Java-ovih API-ja za istodobnost.