Java 101: comprensión de los subprocesos de Java, parte 4: grupos de subprocesos, volatilidad y variables locales de subprocesos

Java 101 de este mes concluye la serie de subprocesos centrándose en los grupos de subprocesos, la volatilidad, las variables locales de subprocesos, los temporizadores y la ThreadDeathclase.

Comprensión de los hilos de Java: lea la serie completa

  • Parte 1: Introducción a subprocesos y ejecutables
  • Parte 2: sincronización de subprocesos
  • Parte 3: programación de subprocesos, espera / notificación e interrupción de subprocesos
  • Parte 4: grupos de subprocesos, volatilidad, variables locales de subprocesos, temporizadores y muerte de subprocesos

Grupos de hilos

En un programa de servidor de red, un subproceso espera y acepta solicitudes de programas cliente para ejecutar, por ejemplo, transacciones de base de datos o cálculos complejos. El hilo normalmente crea un hilo nuevo para manejar la solicitud. Dependiendo del volumen de solicitudes, pueden estar presentes simultáneamente muchos subprocesos diferentes, lo que complica la administración de subprocesos. Para simplificar la gestión de subprocesos, los programas organizan sus subprocesos con grupos de subprocesos : java.lang.ThreadGroupobjetos que agrupan los objetos Thread(y Threadsubclase) de subprocesos relacionados . Por ejemplo, su programa puede usar ThreadGrouppara agrupar todos los hilos de impresión en un grupo.

Nota: Para simplificar la discusión, me refiero a los grupos de hilos como si organizaran hilos. En realidad, los grupos de subprocesos organizan Thread(y Threadsubclasifican) objetos asociados con subprocesos.

Java requiere que todos los subprocesos y todos los grupos de subprocesos, salvo el grupo de subprocesos raíz, se systemunan a otro grupo de subprocesos. Esa disposición conduce a una estructura jerárquica de grupos de subprocesos, que la figura siguiente ilustra en un contexto de aplicación.

En la parte superior de la estructura de la figura está el systemgrupo de hilos. El systemgrupo creado por JVM organiza los subprocesos de JVM que se ocupan de la finalización de objetos y otras tareas del sistema, y ​​sirve como grupo de subprocesos raíz de la estructura jerárquica del grupo de subprocesos de una aplicación. Justo debajo systemestá el maingrupo de subprocesos creado por JVM , que es systemel grupo de subprocesos (subgrupo, para abreviar). maincontiene al menos un subproceso: el subproceso principal creado por JVM que ejecuta instrucciones de código de bytes en el main()método.

Debajo del maingrupo se encuentran los subgrupos subgroup 1y subgroup 2, subgrupos creados por la aplicación (que crea la aplicación de la figura). Además, subgroup 1grupos de tres hilos de aplicación creados: thread 1, thread 2, y thread 3. En contraste, subgroup 2los grupos de un hilo de aplicación creado: my thread.

Ahora que conoce los conceptos básicos, comencemos a crear grupos de hilos.

Cree grupos de hilos y asocie hilos con esos grupos

La ThreadGroupdocumentación del SDK de la clase revela dos constructores: ThreadGroup(String name)y ThreadGroup(ThreadGroup parent, String name). Ambos constructores crean un grupo de hilos y le dan un nombre, como nameespecifica el parámetro. Los constructores difieren en su elección de qué grupo de hilos sirve como padre del grupo de hilos recién creado. Cada grupo de subprocesos, excepto system, debe tener un grupo de subprocesos principal. Porque ThreadGroup(String name), el padre es el grupo de subprocesos del subproceso que llama ThreadGroup(String name). Por ejemplo, si el hilo principal llama ThreadGroup(String name), el grupo de hilos recién creado tiene el grupo del hilo principal como padre— main. Porque ThreadGroup(ThreadGroup parent, String name), el padre es el grupo al que hace parentreferencia. El siguiente código muestra cómo usar estos constructores para crear un par de grupos de hilos:

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

En el código anterior, el hilo principal crea dos grupos de hilos: Ay B. Primero, el hilo principal se crea Allamando ThreadGroup(String name). El tg1padre del grupo de hilos al que se hace referencia es mainporque maines el grupo de hilos del hilo principal. En segundo lugar, el hilo principal crea Bllamando ThreadGroup(ThreadGroup parent, String name). La tg2matriz de grupo de hilos -referenced es Adebido tg1referencia 's pasa como argumento a ThreadGroup (tg1, "B")y Aasociados con tg1.

Sugerencia: una vez que ya no necesite una jerarquía de ThreadGroupobjetos, llame ThreadGroupal void destroy()método mediante una referencia al ThreadGroupobjeto en la parte superior de esa jerarquía. Si el ThreadGroupobjeto superior y todos los objetos del subgrupo carecen de objetos de hilo, destroy()prepara esos objetos de grupo de hilo para la recolección de basura. De lo contrario, destroy()lanza un IllegalThreadStateExceptionobjeto. Sin embargo, hasta que anule la referencia al ThreadGroupobjeto superior (asumiendo que una variable de campo contiene esa referencia), el recolector de basura no puede recolectar ese objeto. Haciendo referencia al objeto superior, puede determinar si se realizó una llamada anterior al destroy()método llamando ThreadGroupal boolean isDestroyed()método de. Ese método devuelve verdadero si se destruyó la jerarquía del grupo de subprocesos.

Por sí mismos, los grupos de hilos son inútiles. Para ser de alguna utilidad, deben agrupar subprocesos. Puede agrupar subprocesos en grupos de subprocesos pasando ThreadGroupreferencias a los Threadconstructores apropiados :

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

El código anterior primero crea un subgroup 2grupo con mainel grupo principal. (Supongo que el hilo principal ejecuta el código). El siguiente código crea un my threadThreadobjeto en el subgroup 2grupo.

Ahora, creemos una aplicación que produzca la estructura jerárquica de grupos de hilos de nuestra figura:

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

ThreadGroupDemocrea el grupo de hilos apropiado y los objetos de hilo para reflejar lo que ves en la figura anterior. Para probar que los grupos subgroup 1y subgroup 2son mainlos únicos subgrupos, ThreadGroupDemohaga lo siguiente:

  1. Recupera una referencia a del hilo principal ThreadGroupobjeto llamando Thread'estática s currentThread()método (que devuelve una referencia a del hilo principal Threadobjeto) seguidos por Thread' s ThreadGroup getThreadGroup()método.
  2. Llama ThreadGroupal int activeGroupCount()método de la ThreadGroupreferencia recién devuelta para devolver una estimación de los grupos activos dentro del grupo de subprocesos del hilo principal.
  3. Llama ThreadGroupal String getName ()método para devolver el nombre del grupo de hilos del hilo principal.
  4. ThreadGroupEl void list ()método de las llamadas para imprimir en los detalles del dispositivo de salida estándar en el grupo de subprocesos del hilo principal y todos los subgrupos.

Cuando se ejecuta, ThreadGroupDemomuestra la siguiente salida:

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]

Salida que comienza con los Threadresultados de list()las llamadas internas de Thread'al toString()método de', un formato de salida que describí en la Parte 1. Junto con esa salida, verá la salida que comienza con java.lang.ThreadGroup. Esa salida identifica el nombre del grupo de subprocesos seguido de su máxima prioridad.

Grupos de prioridad y subprocesos

La máxima prioridad de un grupo de subprocesos es la prioridad más alta que cualquiera de sus subprocesos puede alcanzar. Considere el programa de servidor de red mencionado anteriormente. Dentro de ese programa, un hilo espera y acepta solicitudes de programas cliente. Antes de hacer eso, el hilo de espera / aceptación-solicitud puede crear primero un grupo de hilos con una prioridad máxima justo debajo de la prioridad de ese hilo. Más tarde, cuando llega una solicitud, el subproceso de espera / aceptación de solicitud crea un nuevo subproceso para responder a la solicitud del cliente y agrega el nuevo subproceso al grupo de subprocesos creado anteriormente. La prioridad del nuevo hilo se reduce automáticamente al máximo del grupo de hilos. De esa manera, el subproceso de espera / aceptación de solicitud responde con más frecuencia a las solicitudes porque se ejecuta con más frecuencia.

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

ThreadGroupEl interrupt()método de 'permite que un hilo interrumpa los hilos y subgrupos de un grupo de hilos específico. Esta técnica resultaría apropiada en el siguiente escenario: El hilo principal de su aplicación crea varios hilos y cada uno realiza una unidad de trabajo. Debido a que todos los subprocesos deben completar sus respectivas unidades de trabajo antes de que cualquier subproceso pueda examinar los resultados, cada subproceso espera después de completar su unidad de trabajo. El hilo principal monitorea el estado de trabajo. Una vez que todos los demás hilos están esperando, el hilo principal llama interrupt()para interrumpir las esperas de los demás hilos. Entonces esos hilos pueden examinar y procesar los resultados. El Listado 3 demuestra la interrupción del grupo de subprocesos:

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