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áken—java.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ém
Skupina 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 tg1
Odkaz 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 ThreadGroup
je 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 ThreadGroup
je 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ákno
Vlá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í:
- Načte odkaz na hlavní vlákno
ThreadGroup
objekt volánímVlákno
je statickýcurrentThread ()
metoda (která vrací odkaz na hlavní vláknoVlákno
objekt) následovanýVlákno
jeThreadGroup getThreadGroup ()
metoda. - Volání
ThreadGroup
jeint activeGroupCount ()
metoda na právě vrácenéThreadGroup
odkaz na vrácení odhadu aktivních skupin ve skupině vláken hlavního vlákna. - Volání
ThreadGroup
jeŘetězec getName ()
metoda pro návrat názvu skupiny vláken hlavního vlákna. - Volání
ThreadGroup
jeseznam 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ákno
je 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í ThreadGroup
je 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 ThreadGroup
je 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 Y
Priorita 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
ThreadGroup
je 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í."); }}}