Programování

Chování vláken v JVM

Závitování odkazuje na postup souběžného provádění programovacích procesů za účelem zlepšení výkonu aplikace. I když to není tak běžné pro práci s vlákny přímo v podnikových aplikacích, používají se po celou dobu v rámci Java.

Například rámce, které zpracovávají velké množství informací, například Spring Batch, používají vlákna ke správě dat. Současná manipulace s vlákny nebo procesy CPU zvyšuje výkon, což vede k rychlejším a efektivnějším programům.

Získejte zdrojový kód

Získejte kód pro tento Java Challenger. Při provádění příkladů můžete spustit vlastní testy.

Najděte své první vlákno: metoda main () Java

I když jste nikdy nepracovali přímo s vlákny Java, pracovali jste s nimi nepřímo, protože metoda main () Java obsahuje hlavní vlákno. Kdykoli jste provedli hlavní() metoda, provedli jste také hlavní Vlákno.

Studium Vlákno třída je velmi užitečná pro pochopení toho, jak threading funguje v programech Java. Můžeme přistupovat k vláknu, které se provádí, vyvoláním currentThread (). getName () metoda, jak je znázorněno zde:

 veřejná třída MainThread {public static void main (String ... mainThread) {System.out.println (Thread.currentThread (). getName ()); }} 

Tento kód vytiskne „hlavní“ a identifikuje právě prováděné vlákno. Znalost identifikace prováděného vlákna je prvním krokem k absorbování konceptů vlákna.

Životní cyklus vlákna Java

Při práci s vlákny je důležité mít na paměti stav vlákna. Životní cyklus podprocesu Java se skládá ze šesti stavů podprocesů:

  • Nový: Nový Vlákno() byla vytvořena instance.
  • Spustitelný: The Vláknoje Start() byla vyvolána metoda.
  • Běh: The Start() byla vyvolána metoda a vlákno běží.
  • Pozastaveno: Vlákno je dočasně pozastaveno a může být obnoveno jiným vláknem.
  • Blokováno: Vlákno čeká na příležitost ke spuštění. To se stane, když jedno vlákno již vyvolalo synchronizované () metoda a další vlákno musí počkat, až bude hotové.
  • Ukončeno: Provádění vlákna je dokončeno.
Rafael Chinelato Del Nero

O stavech podprocesů je třeba prozkoumat a pochopit více, ale informace na obrázku 1 vám stačí k vyřešení této výzvy Java.

Souběžné zpracování: Rozšíření třídy vlákna

V nejjednodušším případě se souběžné zpracování provádí rozšířením a Vlákno třída, jak je uvedeno níže.

 veřejná třída InheritingThread rozšiřuje Thread {InheritingThread (String 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 () + "is running"); }} 

Tady běží dvě vlákna: MainThread a InheritingThread. Když vyvoláme Start() metoda s novým inheritingThread (), logika v běh() metoda je provedena.

Také předáme název druhého vlákna v Vlákno konstruktor třídy, takže výstup bude:

 hlavní běží. inheritingThread běží. 

Spustitelné rozhraní

Spíše než pomocí dědičnosti můžete implementovat rozhraní Runnable. Přihrávka Spustitelný uvnitř a Vlákno Výsledkem konstruktoru je menší vazba a větší flexibilita. Po průchodu Spustitelný, můžeme vyvolat Start() metoda přesně jako v předchozím příkladu:

 veřejná třída RunnableThread implementuje Runnable {public static void main (String ... runnableThread) {System.out.println (Thread.currentThread (). getName ()); new Thread (new RunnableThread ()). start (); } @Override public void run () {System.out.println (Thread.currentThread (). GetName ()); }} 

Vlákna bez démona vs. démona

Pokud jde o provedení, existují dva typy vláken:

  • Vlákna bez démona jsou prováděny až do konce. Hlavní vlákno je dobrým příkladem vlákna bez démona. Kód v hlavní() budou provedeny vždy až do konce, pokud a System.exit () vynutí dokončení programu.
  • A vlákno démona je pravý opak, v zásadě jde o proces, který se nemusí provádět až do konce.

Pamatujte na pravidlo: Pokud končící nedémonské vlákno končí před vláknem démona, vlákno démona nebude provedeno až do konce.

Abyste lépe porozuměli vztahu vláken démonů a nedémonů, prostudujte si tento příklad:

 import java.util.stream.IntStream; public class NonDaemonAndDaemonThread {public static void main (String ... nonDaemonAndDaemon) throws InterruptedException {System.out.println ("Zahájení provádění ve vlákně" + Thread.currentThread (). getName ()); Thread daemonThread = new Thread (() -> IntStream.rangeClosed (1, 100000) .forEach (System.out :: println)); daemonThread.setDaemon (true); daemonThread.start (); Závit. Spánek (10); System.out.println ("Konec provádění ve vlákně" + Thread.currentThread (). GetName ()); }} 

V tomto příkladu jsem použil vlákno démona k deklaraci rozsahu od 1 do 100 000, všechny iteroval a poté tiskl. Nezapomeňte však, že démonské vlákno nedokončí spuštění, pokud hlavní vlákno nedémonů skončí jako první.

Výstup bude probíhat následovně:

  1. Začátek provádění v hlavním vlákně.
  2. Tisk čísel od 1 do 100 000.
  3. Konec provádění v hlavním vlákně, velmi pravděpodobně před dokončením iterace na 100 000.

Konečný výstup bude záviset na vaší implementaci JVM.

A to mě přivádí k dalšímu bodu: vlákna jsou nepředvídatelná.

Priorita vlákna a JVM

Je možné upřednostnit provádění podprocesu pomocí setPriority metoda, ale to, jak se s ní zachází, závisí na implementaci JVM. Linux, MacOS a Windows mají různé implementace JVM a každý bude zpracovávat prioritu vlákna podle svých vlastních výchozích hodnot.

Nastavená priorita vlákna však ovlivňuje pořadí vyvolání vlákna. Tři konstanty deklarované v Vlákno třídy jsou:

 / ** * Minimální priorita, kterou může mít vlákno. * / public static final int MIN_PRIORITY = 1; / ** * Výchozí priorita přiřazená vláknu. * / public static final int NORM_PRIORITY = 5; / ** * Maximální priorita, kterou může mít vlákno. * / public static final int MAX_PRIORITY = 10; 

Zkuste spustit několik testů na následujícím kódu, abyste zjistili, s jakou prioritou spuštění skončíte:

 public class ThreadPriority {public static void main (String ... threadPriority) {Thread moeThread = new Thread (() -> System.out.println ("Moe")); Thread barneyThread = new Thread (() -> 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 (); }} 

I když jsme nastavili moeThread tak jako MAX_PRIORITY, nemůžeme počítat s tím, že toto vlákno bude provedeno jako první. Místo toho bude pořadí provádění náhodné.

Konstanty vs výčty

The Vlákno třída byla představena s Java 1.0. V té době byly priority stanoveny pomocí konstant, nikoli enumů. Existuje však problém s používáním konstant: pokud předáme číslo priority, které není v rozsahu 1 až 10, bude setPriority () metoda vyvolá IllegalArgumentException. Dnes můžeme tento problém vyřešit pomocí enumů. Použití enums znemožňuje předání nelegálního argumentu, což zjednodušuje kód a poskytuje nám větší kontrolu nad jeho spuštěním.

Přijměte výzvu pro vlákna Java!

Dozvěděli jste se jen málo o vláknech, ale pro výzvu Java tohoto příspěvku to stačí.

Nejprve si prostudujte následující kód:

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

Jaký bude výstup tohoto kódu? Analyzujte kód a zkuste si sami určit odpověď na základě toho, co jste se naučili.

A. Harley Davidson

B. Dodge Tomahawk

C. Yamaha YZF

D. Neurčité

Co se právě stalo? Porozumění chování vláken

Ve výše uvedeném kódu jsme vytvořili tři vlákna. První vlákno je Harley Davidsona tomuto vláknu jsme přiřadili výchozí prioritu. Druhé vlákno je Dodge Tomahawk, přiřazeno MAX_PRIORITY. Třetí je Yamaha YZF, s MIN_PRIORITY. Potom jsme spustili vlákna.

Za účelem určení pořadí, ve kterém budou vlákna spuštěna, si můžete nejprve všimnout, že Motocykl třída rozšiřuje Vlákno třídy a že jsme předali název vlákna v konstruktoru. Také jsme přepsali běh() metoda s podmínkou: pokud je wolverineAdrenalin roven 13.

Přestože Yamaha YZF je třetí vlákno v našem pořadí popravy a má MIN_PRIORITY, neexistuje žádná záruka, že bude proveden jako poslední pro všechny implementace JVM.

Můžete si také všimnout, že v tomto příkladu nastavíme Dodge Tomahawk vlákno jako démon. Protože je to vlákno démona, Dodge Tomahawk nemusí nikdy dokončit provedení. Ale další dvě vlákna jsou ve výchozím nastavení nedemonská, takže Harley Davidson a Yamaha YZF vlákna určitě dokončí jejich provedení.

Závěrem bude výsledek D: Neurčité, protože neexistuje žádná záruka, že plánovač vláken bude sledovat naše pořadí provádění nebo prioritu vlákna.

Nezapomeňte, že se nemůžeme spolehnout na logiku programu (pořadí vláken nebo prioritu vláken) při předpovídání pořadí provádění JVM.

Video výzva! Ladění argumentů proměnných

Ladění je jedním z nejjednodušších způsobů, jak plně absorbovat programovací koncepty a zároveň vylepšit váš kód. V tomto videu můžete sledovat, zatímco ladím a vysvětluji výzvu chování vlákna:

Časté chyby s vlákny Java

  • Vyvolání běh() způsob pokusu o spuštění nového vlákna.
  • Pokus o spuštění vlákna dvakrát (způsobí to IllegalThreadStateException).
  • Umožnění několika procesům změnit stav objektu, když by se neměl měnit.
  • Psaní logiky programu, která se spoléhá na prioritu vlákna (nemůžete ji předvídat).
  • Spoléhání se na pořadí provádění podprocesu - i když nejprve spustíme podproces, neexistuje záruka, že bude provedeno jako první.

Co si pamatovat o vláknech Java

  • Vyvolejte Start() způsob spuštění a Vlákno.
  • Je možné rozšířit Vlákno třídy přímo za účelem použití vláken.
  • Je možné implementovat akci vlákna uvnitř a Spustitelný rozhraní.
  • Priorita vlákna závisí na implementaci JVM.
  • Chování vlákna bude vždy záviset na implementaci JVM.
  • Vlákno démona se nedokončí, pokud nejprve skončí uzavřené vlákno bez démona.

Další informace o vláknech Java v prostředí JavaWorld

  • Přečtěte si řadu podprocesů Java 101 a dozvíte se více o podprocesech a runnablech, synchronizaci podprocesů, plánování podprocesů s čekáním / upozorněním a smrt podprocesů.
  • Modern threading: a Java concurrency primer introduces java.util.concurrent a odpovídá na běžné otázky pro vývojáře, kteří začínají souběžně s Java.
  • Moderní vlákna pro ne zcela začátečníky nabízejí pokročilejší tipy a osvědčené postupy pro práci s nimi java.util.concurrent.

Více od Rafaela

  • Získejte více rychlých tipů na kód: Přečtěte si všechny příspěvky v sérii Java Challengers.
  • Rozvíjejte své znalosti jazyka Java: Navštivte Java Dev Gym a procvičte si kód.
  • Chcete pracovat na projektech bez stresu a psát bezchybný kód? Navštivte NoBugsProject a získejte kopii Žádné chyby, žádný stres - vytvořte software, který změní život, aniž by vám zničil život.

Tento příběh „Chování vláken v JVM“ původně publikoval JavaWorld.

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