Java 101: Comprendre les threads Java, Partie 4: Groupes de threads, volatilité et variables locales de thread

Java 101 de ce mois-ci conclut la série de threads en se concentrant sur les groupes de threads, la volatilité, les variables locales des threads, les minuteries et la ThreadDeathclasse.

Comprendre les threads Java - lire toute la série

  • Partie 1: Présentation des threads et des exécutables
  • Partie 2: Synchronisation des threads
  • Partie 3: Planification des threads, attente / notification et interruption des threads
  • Partie 4: Groupes de threads, volatilité, variables locales de thread, minuteries et mort de thread

Groupes de discussion

Dans un programme de serveur de réseau, un thread attend et accepte les demandes des programmes clients pour exécuter, par exemple, des transactions de base de données ou des calculs complexes. Le thread crée généralement un nouveau thread pour gérer la demande. Selon le volume de requêtes, de nombreux threads différents peuvent être présents simultanément, ce qui complique la gestion des threads. Pour simplifier la gestion des threads, les programmes organisent leurs threads avec des groupes de threads - des java.lang.ThreadGroupobjets qui regroupent les objets des threads Thread(et des Threadsous - classes) associés . Par exemple, votre programme peut utiliser ThreadGrouppour regrouper tous les fils d'impression en un seul groupe.

Remarque: pour simplifier la discussion, je fais référence aux groupes de threads comme s'ils organisaient des threads. En réalité, les groupes de threads organisent Thread(et Threadsous - classes) les objets associés aux threads.

Java nécessite que chaque thread et chaque groupe de threads (enregistrez le groupe de threads racine) systemrejoignent un autre groupe de threads. Cet agencement conduit à une structure hiérarchique de groupe de threads, que la figure ci-dessous illustre dans un contexte d'application.

En haut de la structure de la figure se trouve le systemgroupe de threads. Le systemgroupe créé par JVM organise les threads JVM qui traitent de la finalisation des objets et d'autres tâches système, et sert de groupe de threads racine de la structure hiérarchique du groupe de threads d'une application. Juste en dessous se systemtrouve le maingroupe de threads créé par JVM , qui est systemle sous-groupe de threads (sous-groupe, pour faire court). maincontient au moins un thread: le thread principal créé par JVM qui exécute les instructions d'octet-code dans la main()méthode.

Sous le maingroupe se trouvent les sous subgroup 1- subgroup 2groupes et, les sous-groupes créés par l'application (que l'application de la figure crée). En outre, les subgroup 1groupes trois threads créés par l' application: thread 1, thread 2et thread 3. En revanche, les subgroup 2groupes un nouveau thread application: my thread.

Maintenant que vous connaissez les bases, commençons à créer des groupes de threads.

Créer des groupes de threads et associer des threads à ces groupes

La ThreadGroupdocumentation SDK de la classe révèle deux constructeurs: ThreadGroup(String name)et ThreadGroup(ThreadGroup parent, String name). Les deux constructeurs créent un groupe de threads et lui donnent un nom, comme le nameparamètre le spécifie. Les constructeurs diffèrent dans leur choix du groupe de threads qui sert de parent au groupe de threads nouvellement créé. Chaque groupe de threads, sauf system, doit avoir un groupe de threads parent. Pour ThreadGroup(String name), le parent est le groupe de threads du thread qui appelle ThreadGroup(String name). Par exemple, si le thread principal appelle ThreadGroup(String name), le groupe de threads nouvellement créé a le groupe du thread principal comme parent— main. Pour ThreadGroup(ThreadGroup parent, String name), le parent est le groupe qui fait parentréférence. Le code suivant montre comment utiliser ces constructeurs pour créer une paire de groupes de threads:

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

Dans le code ci-dessus, le thread principal crée deux groupes de threads: Aet B. Tout d'abord, le thread principal crée Aen appelant ThreadGroup(String name). Le tg1parent du groupe de threads référencé est maindû au fait qu'il mains'agit du groupe de threads du thread principal. Deuxièmement, le thread principal crée Ben appelant ThreadGroup(ThreadGroup parent, String name). Le tg2parent du groupe de threads référencé est Adû au fait que tg1la référence de s est transmise comme argument à ThreadGroup (tg1, "B")et As'associe avec tg1.

Astuce: une fois que vous n'avez plus besoin d'une hiérarchie d' ThreadGroupobjets, appelez ThreadGroupla void destroy()méthode de via une référence à l' ThreadGroupobjet en haut de cette hiérarchie. Si l' ThreadGroupobjet supérieur et tous les objets de sous-groupe manquent d'objets de thread, destroy()prépare ces objets de groupe de threads pour le garbage collection. Sinon, destroy()jette un IllegalThreadStateExceptionobjet. Cependant, tant que vous n'annulez pas la référence à l' ThreadGroupobjet supérieur (en supposant qu'une variable de champ contient cette référence), le garbage collector ne peut pas collecter cet objet. En faisant référence à l 'objet supérieur, vous pouvez déterminer si un appel précédent a été effectué à la destroy()méthode en appelant ThreadGroupla boolean isDestroyed()méthode de. Cette méthode renvoie true si la hiérarchie du groupe de threads a été détruite.

En eux-mêmes, les groupes de threads sont inutiles. Pour être utiles, ils doivent regrouper les threads. Vous regroupez les threads en groupes de threads en passant des ThreadGroupréférences aux Threadconstructeurs appropriés :

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

Le code ci-dessus crée d'abord un subgroup 2groupe avec maincomme groupe parent. (Je suppose que le thread principal exécute le code.) Le code crée ensuite un my threadThreadobjet dans le subgroup 2groupe.

Maintenant, créons une application qui produit la structure hiérarchique des groupes de threads de notre figure:

Liste 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 (); } }

ThreadGroupDemocrée le groupe de threads et les objets de thread appropriés pour refléter ce que vous voyez dans la figure ci-dessus. Pour prouver que les groupes subgroup 1et subgroup 2sont mainles seuls sous-groupes de, ThreadGroupDemoprocédez comme suit:

  1. Récupère une référence à l ' ThreadGroupobjet du thread principal en appelant Threadla currentThread()méthode statique de (qui renvoie une référence à l' Threadobjet du thread principal ) suivi de Threadla ThreadGroup getThreadGroup()méthode de.
  2. Appelle ThreadGroupla int activeGroupCount()méthode sur la ThreadGroupréférence qui vient d'être renvoyée pour renvoyer une estimation des groupes actifs dans le groupe de threads du thread principal.
  3. Appelle ThreadGroupla String getName ()méthode pour renvoyer le nom du groupe de threads du thread principal.
  4. Appelle ThreadGroupla void list ()méthode pour imprimer sur le périphérique de sortie standard les détails sur le groupe de threads du thread principal et tous les sous-groupes.

Lors de l'exécution, ThreadGroupDemoaffiche la sortie suivante:

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]

La sortie qui commence par les Threadrésultats list()des appels internes de Threadla toString()méthode de à, un format de sortie que j'ai décrit dans la partie 1. En plus de cette sortie, vous voyez la sortie commençant par java.lang.ThreadGroup. Cette sortie identifie le nom du groupe de threads suivi de sa priorité maximale.

Priorité et groupes de discussion

La priorité maximale d'un groupe de threads est la priorité la plus élevée que l'un de ses threads puisse atteindre. Considérez le programme de serveur de réseau susmentionné. Dans ce programme, un thread attend et accepte les demandes des programmes clients. Avant de faire cela, le thread d'attente / d'acceptation de la demande peut d'abord créer un groupe de threads avec une priorité maximale juste en dessous de la priorité de ce thread. Plus tard, lorsqu'une demande arrive, le thread d'attente / acceptation de demande crée un nouveau thread pour répondre à la demande du client et ajoute le nouveau thread au groupe de threads précédemment créé. La priorité du nouveau thread s'abaisse automatiquement au maximum du groupe de threads. De cette façon, le thread d'attente / acceptation de demande répond plus souvent aux demandes car il s'exécute plus souvent.

Java assigns a maximum priority to each thread group. When you create a group, Java obtains that priority from its parent group. Use ThreadGroup's void setMaxPriority(int priority) method to subsequently set the maximum priority. Any threads that you add to the group after setting its maximum priority cannot have a priority that exceeds the maximum. Any thread with a higher priority automatically lowers when it joins the thread group. However, if you use setMaxPriority(int priority) to lower a group's maximum priority, all threads added to the group prior to that method call keep their original priorities. For example, if you add a priority 8 thread to a maximum priority 9 group, and then lower that group's maximum priority to 7, the priority 8 thread remains at priority 8. At any time, you can determine a thread group's maximum priority by calling ThreadGroup's int getMaxPriority() method. To demonstrate priority and thread groups, I wrote 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 ()); } }

When run, MaxPriorityDemo produces the following output:

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

Thread group A (which tg references) starts with the highest priority (10) as its maximum. Thread X, whose Thread object t1 references, joins the group and receives 5 as its priority. We change that thread's priority to 6, which succeeds because 6 is less than 10. Subsequently, we call setMaxPriority(int priority) to reduce the group's maximum priority to 4. Although thread X remains at priority 6, a newly-added Y thread receives 4 as its priority. Finally, an attempt to increase thread Y's priority to 5 fails, because 5 is greater than 4.

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

ThreadGroupLa interrupt()méthode de permet à un thread d'interrompre les threads et sous-groupes d'un groupe de threads spécifique. Cette technique s'avérerait appropriée dans le scénario suivant: Le thread principal de votre application crée plusieurs threads qui effectuent chacun une unité de travail. Étant donné que tous les threads doivent terminer leurs unités de travail respectives avant qu'un thread puisse examiner les résultats, chaque thread attend après avoir terminé son unité de travail. Le thread principal surveille l'état de travail. Une fois que tous les autres threads sont en attente, le thread principal appelle interrupt()pour interrompre les attentes des autres threads. Ensuite, ces fils peuvent examiner et traiter les résultats. Le listing 3 montre l'interruption du groupe de threads:

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."); } } }