Programování

Java 101: Understanding Java threads, Part 4: Thread groups, volatility, and thread-local variables

Tento měsíc Java 101 uzavírá řadu podprocesů zaměřením na skupiny podprocesů, volatilitu, místní proměnné podprocesů, časovače a ThreadDeath třída.

Porozumění vláknům Java - přečtěte si celou sérii

  • Část 1: Představujeme vlákna a spouštěcí tabulky
  • Část 2: Synchronizace vláken
  • Část 3: Plánování vlákna, čekání / upozornění a přerušení vlákna
  • Část 4: Skupiny vláken, volatilita, místní proměnné vlákna, časovače a smrt vlákna

Skupiny vláken

V programu síťového serveru jedno vlákno čeká a přijímá požadavky od klientských programů k provádění, například databázových transakcí nebo složitých výpočtů. Vlákno obvykle vytvoří nové vlákno pro zpracování požadavku. V závislosti na objemu požadavku může být současně přítomno mnoho různých vláken, což komplikuje správu vláken. Pro zjednodušení správy vláken programy organizují svá vlákna pomocí skupiny vlákenjava.lang.ThreadGroup objekty, které seskupují související vlákna ' Vlákno (a Vlákno podtřídy). Váš program může například použít ThreadGroup seskupit všechna tisková vlákna do jedné skupiny.

Poznámka: Aby byla diskuse jednoduchá, odkazuji na skupiny vláken, jako by organizovaly vlákna. Ve skutečnosti se skupiny vláken organizují Vlákno (a Vlákno podtřídy) objekty spojené s vlákny.

Java vyžaduje každé vlákno a každou skupinu vláken - uložte skupinu kořenových vláken, Systém—Připojit se k nějaké jiné skupině vláken. Toto uspořádání vede k hierarchické struktuře skupiny vláken, kterou níže uvedený obrázek ilustruje v kontextu aplikace.

V horní části struktury postavy je Systém skupina vláken. Vytvořeno JVM Systém group organizuje vlákna JVM, která se zabývají dokončováním objektů a dalšími systémovými úkoly, a slouží jako skupina kořenových vláken struktury hierarchické skupiny vláken aplikace. Přímo pod Systém je vytvořen JVM hlavní skupina vláken, která je SystémSkupina podvlákna (zkráceně podskupina). hlavní obsahuje alespoň jedno vlákno - hlavní vlákno vytvořené JVM, které provádí pokyny v bajtovém kódu v souboru hlavní() metoda.

Pod hlavní skupina sídlí v podskupina 1 a podskupina 2 podskupiny, podskupiny vytvořené aplikací (které aplikace postavy vytváří). Dále podskupina 1 seskupuje tři vlákna vytvořená aplikací: nit 1, nit 2, a nit 3. V porovnání, podskupina 2 seskupuje jedno vlákno vytvořené aplikací: moje vlákno.

Nyní, když znáte základy, začneme vytvářet skupiny vláken.

Vytvářejte skupiny vláken a přidružujte vlákna k těmto skupinám

The ThreadGroup dokumentace SDK třídy odhaluje dva konstruktory: ThreadGroup (název řetězce) a ThreadGroup (ThreadGroup rodič, název řetězce). Oba konstruktory vytvoří skupinu vláken a pojmenují ji jako název parametr určuje. Konstruktory se liší v jejich výběru skupiny vláken, která slouží jako nadřazená pro nově vytvořenou skupinu vláken. Každá skupina vláken, kromě Systém, musí mít nadřazenou skupinu vláken. Pro ThreadGroup (název řetězce), nadřazený je skupina vláken podprocesu, který volá ThreadGroup (název řetězce). Jako příklad, pokud volá hlavní vlákno ThreadGroup (název řetězce), nově vytvořená skupina vláken má jako hlavní nadřazenou skupinu hlavního vlákna—hlavní. Pro ThreadGroup (ThreadGroup rodič, název řetězce), rodič je skupina, která rodič Reference. Následující kód ukazuje, jak použít tyto konstruktory k vytvoření dvojice skupin podprocesů:

public static void main (String [] args) {ThreadGroup tg1 = new ThreadGroup ("A"); ThreadGroup tg2 = nová ThreadGroup (tg1, "B"); }

Ve výše uvedeném kódu vytvoří hlavní vlákno dvě skupiny vláken: A a B. Nejprve se vytvoří hlavní vlákno A voláním ThreadGroup (název řetězce). The tg1-referencovaný rodič skupiny vláken je hlavní protože hlavní je skupina vláken hlavního vlákna. Za druhé se vytvoří hlavní vlákno B voláním ThreadGroup (ThreadGroup rodič, název řetězce). The tg2-referencovaný rodič skupiny vláken je A protože tg1Odkaz předává jako argument ThreadGroup (tg1, "B") a A spolupracovníci s tg1.

Spropitné: Jakmile už nepotřebujete hierarchii ThreadGroup předměty, volejte ThreadGroupje neplatnost zničit () metoda prostřednictvím odkazu na ThreadGroup objekt v horní části této hierarchie. Pokud nahoře ThreadGroup objekt a všechny podskupinové objekty nemají podprocesové objekty, zničit() připravuje tyto objekty skupiny vláken na uvolňování paměti. V opačném případě, zničit() hodí IllegalThreadStateException objekt. Dokud však nezrušíte odkaz na začátek ThreadGroup objekt (za předpokladu, že proměnná pole obsahuje tento odkaz), garbage collector nemůže tento objekt shromáždit. S odkazem na horní objekt můžete určit, zda bylo provedeno předchozí volání na zničit() metoda voláním ThreadGroupje boolean isDestroyed () metoda. Tato metoda vrátí hodnotu true, pokud byla zničena hierarchie skupin vláken.

Samy o sobě jsou skupiny vláken zbytečné. Pro jakékoli použití musí seskupovat vlákna. Seskupujete vlákna do skupin vláken předáním ThreadGroup odkazy na vhodné Vlákno konstruktéři:

ThreadGroup tg = new ThreadGroup ("subgroup 2"); Thread t = new Thread (tg, "my thread");

Výše uvedený kód nejprve vytvoří a podskupina 2 skupina s hlavní jako nadřazená skupina. (Předpokládám, že hlavní vlákno provede kód.) Kód dále vytvoří a moje vláknoVlákno objekt v podskupina 2 skupina.

Nyní vytvořme aplikaci, která vytváří hierarchickou strukturu skupiny vláken naší postavy:

Výpis 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo {public static void main (String [] args) {ThreadGroup tg = new ThreadGroup ("subgroup 1"); Vlákno t1 = nové vlákno (tg, "vlákno 1"); Vlákno t2 = nové vlákno (tg, "vlákno 2"); Vlákno t3 = nové vlákno (tg, "vlákno 3"); tg = new ThreadGroup ("podskupina 2"); Vlákno t4 = nové vlákno (tg, "moje vlákno"); tg = Thread.currentThread () .getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Aktivní skupiny vláken ve skupině vláken" + tg.getName () + ":" + agc); tg.list (); }}

ThreadGroupDemo vytvoří příslušnou skupinu vláken a objekty vláken pro zrcadlení toho, co vidíte na obrázku výše. Dokázat, že podskupina 1 a podskupina 2 skupiny jsou hlavníjsou pouze podskupiny, ThreadGroupDemo dělá následující:

  1. Načte odkaz na hlavní vlákno ThreadGroup objekt voláním Vláknoje statický currentThread () metoda (která vrací odkaz na hlavní vlákno Vlákno objekt) následovaný Vláknoje ThreadGroup getThreadGroup () metoda.
  2. Volání ThreadGroupje int activeGroupCount () metoda na právě vrácené ThreadGroup odkaz na vrácení odhadu aktivních skupin ve skupině vláken hlavního vlákna.
  3. Volání ThreadGroupje Řetězec getName () metoda pro návrat názvu skupiny vláken hlavního vlákna.
  4. Volání ThreadGroupje seznam neplatných () metoda tisku na standardní výstupní zařízení podrobnosti o skupině vláken hlavního vlákna a všech podskupinách.

Při spuštění ThreadGroupDemo zobrazí následující výstup:

Aktivní skupiny vláken v hlavní skupině vláken: 2 java.lang.ThreadGroup [name = main, maxpri = 10] Thread [main, 5, main] Thread [Thread-0,5, main] java.lang.ThreadGroup [name = subgroup 1, maxpri = 10] Vlákno [vlákno 1,5, podskupina 1] Vlákno [vlákno 2,5, podskupina 1] Vlákno [vlákno 3,5, podskupina 1] java.lang.ThreadGroup [name = podskupina 2, maxpri = 10 ] Vlákno [moje vlákno, 5, podskupina 2]

Výstup, který začíná Vlákno výsledky z seznam()interní volání na Vláknoje toString () metoda, výstupní formát, který jsem popsal v části 1. Spolu s tímto výstupem vidíte výstup začínající na java.lang.ThreadGroup. Tento výstup identifikuje název skupiny vláken následovaný maximální prioritou.

Prioritní skupiny a skupiny vláken

Maximální priorita skupiny vláken je nejvyšší prioritou, jaké může její vlákno dosáhnout. Zvažte výše uvedený program síťových serverů. V rámci tohoto programu vlákno čeká a přijímá požadavky z klientských programů. Než to uděláte, vlákno čekání na / přijetí požadavku může nejprve vytvořit skupinu vláken s maximální prioritou těsně pod prioritou tohoto vlákna. Později, když přijde požadavek, vlákno čekání na / přijetí požadavku vytvoří nové vlákno, které bude reagovat na požadavek klienta, a přidá nové vlákno do dříve vytvořené skupiny vláken. Priorita nového vlákna se automaticky sníží na maximum skupiny vláken. Tímto způsobem vlákno čekání na / přijetí požadavku reaguje častěji na požadavky, protože běží častěji.

Java přiřazuje každé skupině vláken maximální prioritu. Když vytvoříte skupinu, získá Java tuto prioritu od své nadřazené skupiny. Použití ThreadGroupje void setMaxPriority (priorita int) metoda pro následné nastavení maximální priority. Vlákna, která do skupiny přidáte po nastavení její maximální priority, nemohou mít prioritu, která přesahuje maximum. Libovolné vlákno s vyšší prioritou se automaticky sníží, když se připojí ke skupině vláken. Pokud však používáte setMaxPriority (priorita int) pro snížení maximální priority skupiny si všechna vlákna přidaná do skupiny před voláním této metody zachovají své původní priority. Například pokud přidáte vlákno s prioritou 8 do skupiny s maximální prioritou 9 a potom snížíte maximální prioritu této skupiny na 7, vlákno s prioritou 8 zůstane na prioritě 8. Kdykoli můžete určit maximální prioritu skupiny vláken voláním ThreadGroupje int getMaxPriority () metoda. Abych předvedl skupiny priorit a vláken, napsal jsem MaxPriorityDemo:

Výpis 2. MaxPriorityDemo.java

// MaxPriorityDemo.java třída MaxPriorityDemo {public static void main (String [] args) {ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("maximální priorita tg =" + tg.getMaxPriority ()); Vlákno t1 = nové vlákno (tg, "X"); System.out.println ("priorita t1 =" + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("priorita t1 po setPriority () =" + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("maximální priorita tg po setMaxPriority () =" + tg.getMaxPriority ()); System.out.println ("priorita t1 po setMaxPriority () =" + t1.getPriority ()); Vlákno t2 = nové vlákno (tg, "Y"); System.out.println ("priorita t2 =" + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("priorita t2 po setPriority () =" + t2.getPriority ()); }}

Při spuštění MaxPriorityDemo vytvoří následující výstup:

tg maximální priorita = 10 t1 priorita = 5 t1 priorita po setPriority () = 6 tg maximální priorita po setMaxPriority () = 4 t1 priorita po setMaxPriority () = 6 t2 priorita = 4 t2 priorita po setPriority () = 4

Skupina vláken A (který tg reference) začíná nejvyšší prioritou (10). Vlákno X, jehož Vlákno objekt t1 odkazy, připojí se ke skupině a jako prioritu obdrží 5. Změníme prioritu tohoto vlákna na 6, což je úspěšné, protože 6 je menší než 10. Následně zavoláme setMaxPriority (priorita int) snížit maximální prioritu skupiny na 4. Ačkoli vlákno X zůstává na prioritě 6, nově přidané Y vlákno přijímá 4 jako svou prioritu. Nakonec pokus o zvětšení vlákna YPriorita 5 selže, protože 5 je větší než 4.

Poznámka:setMaxPriority (priorita int) automaticky upravuje maximální prioritu podskupin skupiny vláken.

Kromě použití skupin podprocesů k omezení priority podprocesů můžete provést další úkoly voláním různých ThreadGroup metody, které se vztahují na vlákno každé skupiny. Metody zahrnují void suspend (), neplatný životopis (), neplatná zastávka (), a void interrupt (). Protože společnost Sun Microsystems zastarala první tři metody (jsou nebezpečné), zkoumáme pouze přerušit().

Přerušte skupinu vláken

ThreadGroupje přerušit() metoda umožňuje vláknu přerušit vlákna a podskupiny konkrétní skupiny vláken. Tato technika by se ukázala jako vhodná v následujícím scénáři: Hlavní vlákno vaší aplikace vytváří více vláken, z nichž každý provádí jednotku práce. Protože všechna vlákna musí dokončit své příslušné pracovní jednotky, než může jakékoli vlákno prozkoumat výsledky, každé vlákno čeká po dokončení své pracovní jednotky. Hlavní vlákno sleduje pracovní stav. Jakmile čekají všechna ostatní vlákna, volá hlavní vlákno přerušit() přerušit čekání ostatních vláken. Pak tato vlákna mohou zkoumat a zpracovávat výsledky. Výpis 3 ukazuje přerušení skupiny vláken:

Výpis 3. InterruptThreadGroup.java

// Třída InterruptThreadGroup.java InterruptThreadGroup {public static void main (String [] args) {MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); zkuste {Thread.sleep (2000); // Počkejte 2 sekundy} catch (InterruptedException e) {} // Přeruší všechny metody ve stejné skupině vláken jako hlavní // vlákno Thread.currentThread () .getThreadGroup () .interrupt (); }} class MyThread extends Thread {public void run () {synchronized ("A") {System.out.println (getName () + "about to wait."); zkuste {"A" .wait (); } catch (InterruptedException e) {System.out.println (getName () + "přerušeno."); } System.out.println (getName () + "ukončení."); }}}
$config[zx-auto] not found$config[zx-overlay] not found