Programovanie

Chovanie vlákna v JVM

Závitovanie sa vzťahuje na postup súčasného vykonávania programovacích procesov na zlepšenie výkonu aplikácie. Aj keď nie je také bežné pracovať s vláknami priamo v podnikových aplikáciách, neustále sa používajú v rámci Java.

Napríklad rámce, ktoré spracúvajú veľké množstvo informácií, napríklad Spring Batch, používajú vlákna na správu údajov. Súčasná manipulácia s vláknami alebo procesormi zvyšuje výkon, čo vedie k rýchlejším a efektívnejším programom.

Získajte zdrojový kód

Získajte kód pre tento Java Challenger. Podľa príkladov môžete spustiť svoje vlastné testy.

Nájdite svoje prvé vlákno: metóda main () Java

Aj keď ste nikdy nepracovali priamo s vláknami Java, pracovali ste s nimi nepriamo, pretože metóda main () v jazyku Java obsahuje hlavné vlákno. Kedykoľvek ste vykonali hlavný() metódou, vykonali ste aj hlavnú Závit.

Štúdium Závit trieda je veľmi užitočná na pochopenie toho, ako fungujú vlákna v programoch Java. Prístup k vláknu, ktoré sa vykonáva, môžeme vyvolať vyvolaním súboru currentThread (). getName () zobrazená tu:

 public class MainThread {public static void main (String ... mainThread) {System.out.println (Thread.currentThread (). getName ()); }} 

Tento kód vytlačí „hlavný“ a identifikuje vlákno, ktoré sa práve vykonáva. Vedieť, ako identifikovať vykonávané vlákno, je prvým krokom k absorbovaniu konceptov vlákna.

Životný cyklus vlákna Java

Pri práci s vláknami je dôležité mať na pamäti stav vlákna. Životný cyklus vlákna Java pozostáva zo šiestich stavov vlákna:

  • Nový: Nový Vlákno () bola vytvorená inštancia.
  • Spustiteľné: Závitje štart () bola vyvolaná metóda.
  • Beží: štart () bola vyvolaná metóda a vlákno je spustené.
  • Pozastavené: Vlákno je dočasne pozastavené a môže byť obnovené iným vláknom.
  • Zablokované: Vlákno čaká na príležitosť bežať. To sa stane, keď jedno vlákno už vyvolalo synchronizované () metóda a ďalšie vlákno musí počkať, kým nebude hotové.
  • Ukončené: Vykonanie vlákna je dokončené.
Rafael Chinelato Del Nero

Je tu ešte toho, čo je treba preskúmať a pochopiť o stavoch vlákien, ale informácie na obrázku 1 sú dostatočné na to, aby ste mohli vyriešiť túto výzvu Java.

Súbežné spracovanie: Rozšírenie triedy vlákna

Najjednoduchšie je súčasné spracovanie rozšírením a Závit triedy, ako je uvedené nižšie.

 verejná trieda InheritingThread rozširuje vlákno {InheritingThread (reťazec threadName) {super (threadName); } public static void main (String ... inheriting) {System.out.println (Thread.currentThread (). getName () + "is running"); new InheritingThread ("inheritingThread"). start (); } @Override public void run () {System.out.println (Thread.currentThread (). GetName () + "je spustený"); }} 

Tu prevádzkujeme dve vlákna: MainThread a InheritingThread. Keď vyvoláme štart () metóda s novým inheritingThread (), logika v run () metóda je vykonaná.

Tiež odovzdáme názov druhého vlákna v Závit konštruktor triedy, takže výstup bude:

 hlavný beží. inheritingThread je spustený. 

Spustiteľné rozhranie

Namiesto použitia dedičnosti môžete implementovať rozhranie Runnable. Prihrávka Spustiteľné vo vnútri a Závit Výsledkom konštruktora je menšie prepojenie a väčšia flexibilita. Po prechode Spustiteľné, môžeme vyvolať štart () presne ako v predchádzajúcom príklade:

 verejná trieda RunnableThread implementuje Runnable {public static void main (String ... runnableThread) {System.out.println (Thread.currentThread (). getName ()); nové vlákno (nové RunnableThread ()). start (); } @Override public void run () {System.out.println (Thread.currentThread (). GetName ()); }} 

Vlákna iné ako démon vs.

Pokiaľ ide o vykonávanie, existujú dva typy vlákien:

  • Vlákna, ktoré nie sú démonmi sa vykonávajú až do konca. Hlavné vlákno je dobrým príkladom vlákna iného ako démon. Kód v hlavný() budú vykonané vždy až do konca, pokiaľ a System.exit () núti program dokončiť.
  • A vlákno démona je pravý opak, v podstate ide o proces, ktorý sa nevyžaduje až do konca.

Pamätajte na pravidlo: Ak uzatváracie vlákno bez démonov končí pred vláknom démona, vlákno démona sa vykoná až na konci.

Ak chcete lepšie porozumieť vzťahu vlákien démonov a nedémonov, prečítajte si tento príklad:

 import java.util.stream.IntStream; public class NonDaemonAndDaemonThread {public static void main (String ... nonDaemonAndDaemon) throws InterruptedException {System.out.println ("Starting the execution in the Thread" + Thread.currentThread (). getName ()); Thread daemonThread = new Thread (() -> IntStream.rangeClosed (1, 100000) .forEach (System.out :: println)); daemonThread.setDaemon (true); daemonThread.start (); Závit. Spánok (10); System.out.println ("Koniec vykonávania v rámci vlákna" + Thread.currentThread (). GetName ()); }} 

V tomto príklade som použil démonické vlákno na deklaráciu rozsahu od 1 do 100 000, všetky iteroval a potom vytlačil. Pamätajte však, že démonové vlákno nedokončí vykonávanie, ak hlavné vlákno nedémonov skončí ako prvé.

Výstup bude prebiehať nasledovne:

  1. Začiatok vykonávania v hlavnom vlákne.
  2. Tlačte čísla od 1 do 100 000.
  3. Koniec vykonávania v hlavnom vlákne, veľmi pravdepodobne pred dokončením iterácie na 100 000.

Konečný výstup bude závisieť od vašej implementácie JVM.

A to ma privádza k ďalšiemu bodu: vlákna sú nepredvídateľné.

Priorita vlákna a JVM

Je možné uprednostniť vykonávanie vlákien pomocou setPriority metóda, ale to, ako sa s ňou zaobchádza, závisí od implementácie JVM. Linux, MacOS a Windows majú rôzne implementácie JVM a každý z nich bude spracovávať prioritu vlákna podľa svojich vlastných predvolených nastavení.

Priorita vlákna, ktorú nastavíte, však ovplyvní poradie vyvolania vlákna. Tri konštanty deklarované v Závit triedy sú:

 / ** * Minimálna priorita, ktorú môže mať vlákno. * / verejný statický konečný int MIN_PRIORITY = 1; / ** * Predvolená priorita, ktorá je priradená vláknu. * / public static final int NORM_PRIORITY = 5; / ** * Maximálna priorita, ktorú môže mať vlákno. * / verejný statický konečný int MAX_PRIORITY = 10; 

Skúste spustiť niektoré testy na nasledujúcom kóde, aby ste zistili, s akou prioritou vykonania skončíte:

 public class ThreadPriority {public static void main (String ... threadPriority) {Thread moeThread = new Thread (() -> System.out.println ("Moe")); Vlákno barneyThread = nové vlákno (() -> System.out.println ("Barney")); Thread homerThread = new Thread (() -> System.out.println ("Homer")); moeThread.setPriority (Thread.MAX_PRIORITY); barneyThread.setPriority (Thread.NORM_PRIORITY); homerThread.setPriority (Thread.MIN_PRIORITY); homerThread.start (); barneyThread.start (); moeThread.start (); }} 

Aj keby sme nastavili moeThread ako MAX_PRIORITY, nemôžeme rátať s tým, že toto vlákno bude vykonané ako prvé. Namiesto toho bude poradie vykonania náhodné.

Konštanty vs enumy

The Závit triedy bola predstavená s Java 1.0. V tom čase sa priority stanovovali pomocou konštánt, nie pomocou enumov. S používaním konštánt je však problém: ak odovzdáme číslo priority, ktoré nie je v rozmedzí od 1 do 10, hodnota setPriority () metóda vyvolá IllegalArgumentException. Dnes môžeme tento problém obísť pomocou enumov. Použitie enums znemožňuje odovzdanie nezákonného argumentu, ktorý jednak zjednodušuje kód a jednak nám dáva väčšiu kontrolu nad jeho vykonávaním.

Prijmite výzvu pre vlákna Java!

Dozvedeli ste sa niečo málo o vláknach, ale na výzvu tohto príspevku v jazyku Java to stačí.

Najskôr si prečítajte nasledujúci kód:

 public class ThreadChallenge {private static int wolverineAdrenaline = 10; public static void main (String ... doYourBest) {new Motorcycle ("Harley Davidson"). start (); Motorcycle fastBike = nový motocykel („Dodge Tomahawk“); fastBike.setPriority (Thread.MAX_PRIORITY); fastBike.setDaemon (false); fastBike.start (); Motocykel yamaha = nový motocykel („Yamaha YZF“); yamaha.setPriority (Thread.MIN_PRIORITY); yamaha.start (); } statická trieda Motocykel rozširuje vlákno {Motorcycle (String bikeName) {super (bikeName); } @Override public void run () {wolverineAdrenaline ++; if (wolverineAdrenaline == 13) {System.out.println (this.getName ()); }}}} 

Aký bude výstup tohto kódu? Analyzujte kód a skúste si sami určiť odpoveď na základe toho, čo ste sa naučili.

A. Harley Davidson

B. Dodge Tomahawk

C. Yamaha YZF

D. Neurčité

Čo sa práve stalo? Pochopenie správania vlákien

Vo vyššie uvedenom kóde sme vytvorili tri vlákna. Prvé vlákno je Harley Davidsona tomuto vláknu sme priradili predvolenú prioritu. Druhé vlákno je Dodge Tomahawk, pridelených MAX_PRIORITY. Tretie je Yamaha YZF, s MIN_PRIORITY. Potom sme začali vlákna.

Aby ste určili poradie, v ktorom sa vlákna budú spúšťať, môžete si najskôr uvedomiť, že Motocykel trieda rozširuje Závit triedy a že sme v konštruktore odovzdali názov vlákna. Tiež sme prepísali run () metóda s podmienkou: ak je wolverineAdrenalin rovný 13.

Aj napriek tomu Yamaha YZF je tretie vlákno v našom poradí vykonania a má MIN_PRIORITY, neexistuje žiadna záruka, že bude vykonaný posledný pre všetky implementácie JVM.

Môžete si tiež všimnúť, že v tomto príklade sme nastavili Dodge Tomahawk závit ako démon. Pretože je to vlákno démonov, Dodge Tomahawk nemusí nikdy dokončiť popravu. Ale ďalšie dve vlákna sú predvolene nedémonické, takže Harley Davidson a Yamaha YZF vlákna definitívne dokončia svoje vykonanie.

Záverom bude, že výsledok bude D: Neurčité, pretože neexistuje žiadna záruka, že plánovač vlákien bude sledovať naše poradie vykonania alebo prioritu vlákna.

Pamätajte, že sa nemôžeme spoliehať na logiku programu (poradie vlákien alebo priorita vlákna) pri predpovedaní poradia vykonania JVM.

Video výzva! Ladenie argumentov premenných

Ladenie je jedným z najjednoduchších spôsobov, ako úplne absorbovať programovacie koncepty a zároveň vylepšiť váš kód. V tomto videu môžete sledovať, kým ladím a vysvetľujem výzvu správania vlákna:

Bežné chyby s vláknami Java

  • Vyvolávanie run () metóda pokúsiť sa založiť nové vlákno.
  • Pokus o spustenie vlákna dvakrát (spôsobí to IllegalThreadStateException).
  • Umožnenie viacerým procesom zmeniť stav objektu, keď by sa nemal meniť.
  • Logika programovania, ktorá sa spolieha na prioritu vlákna (nemôžete ju predvídať).
  • Spoliehanie sa na poradie vykonávania vlákna - aj keď najskôr začneme vlákno, neexistuje záruka, že sa vykoná ako prvé.

Čo si pamätať o vláknach Java

  • Vyvolajte štart () spôsob spustenia a Závit.
  • Je možné predĺžiť Závit triedy priamo za účelom použitia vlákien.
  • Je možné implementovať akciu vlákna vo vnútri a Spustiteľné rozhranie.
  • Priorita vlákna závisí od implementácie JVM.
  • Chovanie vlákna bude vždy závisieť od implementácie JVM.
  • Vlákno démona sa nedokončí, ak najskôr skončí priložené vlákno iné ako démon.

Získajte viac informácií o vláknach Java v prostredí JavaWorld

  • Prečítajte si sériu vlákien Java 101, kde sa dozviete viac o vláknach a spustiteľných zostavách, synchronizácii vlákien, plánovaní vlákien s čakaním / upozornením a smrti vlákna.
  • Moderné vlákna: predstavuje súbežný primer Java java.util.concurrent a odpovedá na bežné otázky vývojárov, ktorí sú v súbežnosti Java noví.
  • Moderné vlákna pre nie úplne začiatočníkov ponúkajú pokročilejšie tipy a osvedčené postupy pri práci s nimi java.util.concurrent.

Viac od Rafaela

  • Získajte viac rýchlych tipov na kód: Prečítajte si všetky príspevky v sérii Java Challengers.
  • Budujte svoje znalosti jazyka Java: Navštívte Java Dev Gym a trénujte kód.
  • Chcete pracovať na projektoch bez stresu a písať bezchybný kód? Navštívte NoBugsProject, kde nájdete svoju kópiu Žiadne chyby, žiadny stres - vytvorte si softvér, ktorý zmení život, bez toho, aby vám zničil život.

Tento príbeh „Chovanie vlákna v JVM“ pôvodne publikoval server JavaWorld.

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