Java 101: Grundlegendes zu Java-Threads, Teil 4: Thread-Gruppen, Volatilität und thread-lokale Variablen

Java 101 dieses Monats schließt die Thread-Reihe ab, indem es sich auf Thread-Gruppen, Volatilität, Thread-lokale Variablen, Timer und die ThreadDeathKlasse konzentriert.

Grundlegendes zu Java-Threads - Lesen Sie die gesamte Serie

  • Teil 1: Einführung in Threads und Runnables
  • Teil 2: Thread-Synchronisation
  • Teil 3: Thread-Planung, Warten / Benachrichtigen und Thread-Unterbrechung
  • Teil 4: Thread-Gruppen, Volatilität, Thread-lokale Variablen, Timer und Thread-Tod

Thread-Gruppen

In einem Netzwerkserverprogramm wartet ein Thread auf Anforderungen von Clientprogrammen und akzeptiert diese, um beispielsweise Datenbanktransaktionen oder komplexe Berechnungen auszuführen. Der Thread erstellt normalerweise einen neuen Thread, um die Anforderung zu verarbeiten. Abhängig vom Anforderungsvolumen können viele verschiedene Threads gleichzeitig vorhanden sein, was die Thread-Verwaltung erschwert. Um die Thread-Verwaltung zu vereinfachen, organisieren Programme ihre Threads mit Thread-Gruppen - java.lang.ThreadGroupObjekten, die die Objekte verwandter Threads Thread(und ThreadUnterklassen) gruppieren . Mit Ihrem Programm können Sie beispielsweise ThreadGroupalle Druckthreads in einer Gruppe zusammenfassen.

Hinweis: Um die Diskussion einfach zu halten, verweise ich auf Thread-Gruppen, als ob sie Threads organisieren. In der Realität organisieren Thread-Gruppen Objekte Thread(und ThreadUnterklassen), die Threads zugeordnet sind.

Java erfordert, dass jeder Thread und jede Thread-Gruppe - speichern Sie die Root-Thread-Gruppe system- einer anderen Thread-Gruppe beitritt. Diese Anordnung führt zu einer hierarchischen Thread-Gruppenstruktur, die in der folgenden Abbildung in einem Anwendungskontext dargestellt ist.

Am oberen Rand der Struktur der Figur befindet sich die systemFadengruppe. Die von JVM erstellte systemGruppe organisiert JVM-Threads, die sich mit der Objektfinalisierung und anderen Systemaufgaben befassen, und dient als Stamm-Thread-Gruppe der hierarchischen Thread-Gruppenstruktur einer Anwendung. Direkt darunter systembefindet sich die von JVM erstellte mainThread-Gruppe, bei der es sich um systemdie Unterfadengruppe handelt (kurz Untergruppe). mainenthält mindestens einen Thread - den von der JVM erstellten Hauptthread, der Bytecode-Anweisungen in der main()Methode ausführt .

Unterhalb der mainGruppe befinden sich die Untergruppen subgroup 1und subgroup 2, von Anwendungen erstellte Untergruppen (die von der Anwendung der Abbildung erstellt werden). Weiterhin subgroup 1Gruppen drei anwendungs erstellt Themen: thread 1, thread 2, und thread 3. Im Gegensatz dazu subgroup 2gruppiert ein von einer Anwendung erstellter Thread : my thread.

Nachdem Sie die Grundlagen kennen, können Sie Thread-Gruppen erstellen.

Erstellen Sie Thread-Gruppen und ordnen Sie diesen Gruppen Threads zu

Die ThreadGroupSDK-Dokumentation der Klasse enthält zwei Konstruktoren: ThreadGroup(String name)und ThreadGroup(ThreadGroup parent, String name). Beide Konstruktoren erstellen eine Thread-Gruppe und geben ihr einen Namen, wie im nameParameter angegeben. Die Konstruktoren unterscheiden sich in der Auswahl, welche Thread-Gruppe der neu erstellten Thread-Gruppe als übergeordnetes Element dient. Mit Ausnahme jeder Thread-Gruppe systemmuss eine übergeordnete Thread-Gruppe vorhanden sein. Denn ThreadGroup(String name)ist die Mutter die Fadengruppe des Threads, Anrufen ThreadGroup(String name). Wenn der Hauptthread beispielsweise aufruft ThreadGroup(String name), hat die neu erstellte Threadgruppe die Gruppe des Hauptthreads als übergeordnetes Element main. Für ThreadGroup(ThreadGroup parent, String name)ist die Mutter die Gruppe , dass parentReferenzen. Der folgende Code zeigt, wie Sie mit diesen Konstruktoren ein Paar von Thread-Gruppen erstellen:

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

Im obigen Code erstellt der Hauptthread zwei Threadgruppen: Aund B. Zunächst wird der Haupt-Thread Adurch Aufrufen erstellt ThreadGroup(String name). Das tg1übergeordnete Element der referenzierten Thread-Gruppe ist, mainweil maines die Thread-Gruppe des Haupt-Threads ist. Zweitens wird der Haupt-Thread Bdurch Aufrufen erstellt ThreadGroup(ThreadGroup parent, String name). Das tg2übergeordnete Element der referenzierten Thread-Gruppe ist, Aweil tg1die Referenz als Argument an übergeben ThreadGroup (tg1, "B")und mit dieser Averknüpft wird tg1.

Tipp: Wenn Sie keine Objekthierarchie mehr benötigen ThreadGroup, rufen Sie ThreadGroupdie void destroy()Methode über einen Verweis auf das ThreadGroupObjekt oben in dieser Hierarchie auf. Wenn dem obersten ThreadGroupObjekt und allen Untergruppenobjekten Thread-Objekte fehlen, werden destroy()diese Thread-Gruppenobjekte für die Speicherbereinigung vorbereitet. Andernfalls wird destroy()ein IllegalThreadStateExceptionObjekt ausgelöst. Solange Sie den Verweis auf das oberste ThreadGroupObjekt nicht annullieren (vorausgesetzt, eine Feldvariable enthält diesen Verweis), kann der Garbage Collector dieses Objekt nicht erfassen. Referenzierung das oberste Objekt, können Sie bestimmen , ob ein vorherigen Aufruf der gemacht wurde destroy()Methode durch den Aufruf ThreadGroup‚s - boolean isDestroyed()Methode. Diese Methode gibt true zurück, wenn die Threadgruppenhierarchie zerstört wurde.

Thread-Gruppen sind für sich genommen nutzlos. Um von Nutzen zu sein, müssen sie Threads gruppieren. Sie gruppieren Threads in Thread-Gruppen, indem Sie ThreadGroupVerweise an entsprechende ThreadKonstruktoren übergeben:

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

Der obige Code erstellt zuerst eine subgroup 2Gruppe mit mainder übergeordneten Gruppe. (Ich gehe davon aus, dass der Hauptthread den Code ausführt.) Der Code erstellt als nächstes ein my threadThreadObjekt in der subgroup 2Gruppe.

Erstellen wir nun eine Anwendung, die die hierarchische Thread-Gruppenstruktur unserer Figur erzeugt:

Listing 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("subgroup 1"); Thread t1 = new Thread (tg, "thread 1"); Thread t2 = new Thread (tg, "thread 2"); Thread t3 = new Thread (tg, "thread 3"); tg = new ThreadGroup ("subgroup 2"); Thread t4 = new Thread (tg, "my thread"); tg = Thread.currentThread ().getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Active thread groups in " + tg.getName () + " thread group: " + agc); tg.list (); } }

ThreadGroupDemoErstellt die entsprechende Thread-Gruppe und Thread-Objekte, um das zu spiegeln, was Sie in der obigen Abbildung sehen. Gehen Sie wie folgt vor, um zu beweisen, dass die subgroup 1und subgroup 2-Gruppen die maineinzigen Untergruppen sind ThreadGroupDemo:

  1. Ruft einen Verweis auf das ThreadGroupObjekt des Hauptthreads ab , indem die Threadstatische currentThread()Methode (die einen Verweis auf das ThreadObjekt des Hauptthreads zurückgibt ) gefolgt von Threadder ThreadGroup getThreadGroup()Methode von aufgerufen wird.
  2. Ruft ThreadGroupdie int activeGroupCount()Methode für die gerade zurückgegebene ThreadGroupReferenz auf, um eine Schätzung der aktiven Gruppen innerhalb der Threadgruppe des Hauptthreads zurückzugeben.
  3. Ruft ThreadGroupdie String getName ()Methode auf, um den Threadgruppennamen des Hauptthreads zurückzugeben.
  4. Ruft ThreadGroupdie void list ()Methode zum Aufrufen der Details des Standardausgabegeräts in der Threadgruppe des Hauptthreads und in allen Untergruppen auf.

Zeigt beim Ausführen ThreadGroupDemodie folgende Ausgabe an:

Active thread groups in main thread group: 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] Thread[thread 1,5,subgroup 1] Thread[thread 2,5,subgroup 1] Thread[thread 3,5,subgroup 1] java.lang.ThreadGroup[name=subgroup 2,maxpri=10] Thread[my thread,5,subgroup 2]

Ausgang, der mit beginnt Threadergibt sich aus list()den internen Anrufe Thread's toString()Methode, ein Ausgabeformat I in Teil 1 beschrieben Zusammen mit diesem Ausgang sehen Sie Ausgabe beginnend mit java.lang.ThreadGroup. Diese Ausgabe identifiziert den Namen der Thread-Gruppe, gefolgt von ihrer maximalen Priorität.

Prioritäts- und Thread-Gruppen

Die maximale Priorität einer Thread-Gruppe ist die höchste Priorität, die ein Thread erreichen kann. Betrachten Sie das oben genannte Netzwerkserverprogramm. Innerhalb dieses Programms wartet ein Thread auf Anforderungen von Client-Programmen und akzeptiert diese. Zuvor erstellt der Thread "Warten auf / Akzeptieren-Anfordern" möglicherweise zuerst eine Thread-Gruppe mit einer maximalen Priorität direkt unter der Priorität dieses Threads. Später, wenn eine Anfrage eintrifft, erstellt der Thread "Warten auf / Akzeptieren-Anfrage" einen neuen Thread, um auf die Client-Anfrage zu antworten, und fügt den neuen Thread der zuvor erstellten Thread-Gruppe hinzu. Die Priorität des neuen Threads wird automatisch auf das Maximum der Thread-Gruppe gesenkt. Auf diese Weise reagiert der Thread "Warten auf / Akzeptieren von Anforderungen" häufiger auf Anforderungen, da er häufiger ausgeführt wird.

Java weist jeder Thread-Gruppe eine maximale Priorität zu. Wenn Sie eine Gruppe erstellen, erhält Java diese Priorität von seiner übergeordneten Gruppe. Verwenden Sie ThreadGroupdie void setMaxPriority(int priority)Methode von, um anschließend die maximale Priorität festzulegen. Threads, die Sie der Gruppe hinzufügen, nachdem Sie ihre maximale Priorität festgelegt haben, dürfen keine Priorität haben, die die maximale Priorität überschreitet. Jeder Thread mit einer höheren Priorität wird automatisch verringert, wenn er der Thread-Gruppe beitritt. Wenn Sie jedoch setMaxPriority(int priority)die maximale Priorität einer Gruppe verringern, behalten alle Threads, die der Gruppe vor diesem Methodenaufruf hinzugefügt wurden, ihre ursprünglichen Prioritäten bei. Wenn Sie beispielsweise einer Gruppe mit maximaler Priorität 9 einen Thread mit Priorität 8 hinzufügen und dann die maximale Priorität dieser Gruppe auf 7 senken, bleibt der Thread mit Priorität 8 auf Priorität 8. Sie können jederzeit eine Thread-Gruppe bestimmen. 's maximale Priorität durch Aufrufen von ThreadGroup'sint getMaxPriority()Methode. Um Priorität und Thread-Gruppen zu demonstrieren, schrieb ich MaxPriorityDemo:

Listing 2. MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg maximum priority = " + tg.getMaxPriority ()); Thread t1 = new Thread (tg, "X"); System.out.println ("t1 priority = " + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("t1 priority after setPriority() = " + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("tg maximum priority after setMaxPriority() = " + tg.getMaxPriority ()); System.out.println ("t1 priority after setMaxPriority() = " + t1.getPriority ()); Thread t2 = new Thread (tg, "Y"); System.out.println ("t2 priority = " + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("t2 priority after setPriority() = " + t2.getPriority ()); } }

MaxPriorityDemoErzeugt beim Ausführen die folgende Ausgabe:

tg maximum priority = 10 t1 priority = 5 t1 priority after setPriority() = 6 tg maximum priority after setMaxPriority() = 4 t1 priority after setMaxPriority() = 6 t2 priority = 4 t2 priority after setPriority() = 4

Die Thread-Gruppe A(auf die tgverwiesen wird) beginnt mit der höchsten Priorität (10) als Maximum. Thread X, dessen ThreadObjekt t1referenziert, tritt der Gruppe bei und erhält 5 als Priorität. Wir ändern die Priorität dieses Threads in 6, was erfolgreich ist, weil 6 kleiner als 10 ist. Anschließend rufen wir setMaxPriority(int priority)auf, die maximale Priorität der Gruppe auf 4 zu reduzieren. Obwohl der Thread Xauf Priorität 6 bleibt, Yerhält ein neu hinzugefügter Thread 4 als Priorität. Schließlich Yschlägt ein Versuch fehl, die Priorität des Threads auf 5 zu erhöhen , da 5 größer als 4 ist.

Note:setMaxPriority(int priority) automatically adjusts the maximum priority of a thread group's subgroups.

In addition to using thread groups to limit thread priority, you can accomplish other tasks by calling various ThreadGroup methods that apply to each group's thread. Methods include void suspend(), void resume(), void stop(), and void interrupt(). Because Sun Microsystems has deprecated the first three methods (they are unsafe), we examine only interrupt().

Interrupt a thread group

ThreadGroupMit dieser interrupt()Methode kann ein Thread die Threads und Untergruppen einer bestimmten Thread-Gruppe unterbrechen. Diese Technik würde sich im folgenden Szenario als angemessen erweisen: Der Hauptthread Ihrer Anwendung erstellt mehrere Threads, die jeweils eine Arbeitseinheit ausführen. Da alle Threads ihre jeweiligen Arbeitseinheiten vervollständigen müssen, bevor ein Thread die Ergebnisse überprüfen kann, wartet jeder Thread nach Abschluss seiner Arbeitseinheit. Der Haupt-Thread überwacht den Arbeitsstatus. Sobald alle anderen Threads warten, ruft der Haupt-Thread auf, um die Wartezeiten interrupt()der anderen Threads zu unterbrechen. Dann können diese Threads die Ergebnisse untersuchen und verarbeiten. Listing 3 zeigt die Unterbrechung der Thread-Gruppe:

Listing 3. InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup { public static void main (String [] args) { MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); try { Thread.sleep (2000); // Wait 2 seconds } catch (InterruptedException e) { } // Interrupt all methods in the same thread group as the main // thread Thread.currentThread ().getThreadGroup ().interrupt (); } } class MyThread extends Thread { public void run () { synchronized ("A") { System.out.println (getName () + " about to wait."); try { "A".wait (); } catch (InterruptedException e) { System.out.println (getName () + " interrupted."); } System.out.println (getName () + " terminating."); } } }