Grupos de subprocesos en Java

Fondo

Los programas de servidor, como la base de datos y los servidores web, ejecutan repetidamente requests de múltiples clientes y están orientados a procesar una gran cantidad de tareas cortas. Un enfoque para crear una aplicación de servidor sería crear un nuevo subproceso cada vez que llega una solicitud y atender esta nueva solicitud en el subproceso recién creado. Si bien este enfoque parece simple de implementar, tiene desventajas significativas. Un servidor que crea un nuevo subproceso para cada solicitud gastaría más tiempo y consumiría más recursos del sistema creando y destruyendo subprocesos que procesando las requests reales.

Dado que los subprocesos activos consumen recursos del sistema, una JVM que crea demasiados subprocesos al mismo tiempo puede hacer que el sistema se quede sin memoria. Esto requiere la necesidad de limitar el número de subprocesos que se crean.

¿Qué es ThreadPool en Java?

Un grupo de subprocesos reutiliza subprocesos creados previamente para ejecutar tareas actuales y ofrece una solución al problema de la sobrecarga del ciclo de subprocesos y la hiperpaginación de recursos. Dado que el subproceso ya existe cuando llega la solicitud, se elimina el retraso introducido por la creación del subproceso, lo que hace que la aplicación responda mejor.

  • Java proporciona el marco Executor que se centra en la interfaz Executor, su subinterfaz: ExecutorService y la clase ThreadPoolExecutor , que implementa ambas interfaces. Al usar el ejecutor, uno solo tiene que implementar los objetos Runnable y enviarlos al ejecutor para que los ejecute.
  • Le permiten aprovechar los subprocesos, pero centrarse en las tareas que desea que realice el subproceso, en lugar de la mecánica del subproceso.
  • Para usar grupos de subprocesos, primero creamos un objeto de ExecutorService y le pasamos un conjunto de tareas. La clase ThreadPoolExecutor permite establecer el núcleo y el tamaño máximo del grupo. Los ejecutables que ejecuta un subproceso en particular se ejecutan secuencialmente.
    TP Init

    Inicialización del grupo de subprocesos con tamaño = 3 subprocesos. Cola de tareas = 5 objetos ejecutables

  • Métodos del grupo de subprocesos del ejecutor

Method                         Description
newFixedThreadPool(int)           Creates a fixed size thread pool.
newCachedThreadPool()             Creates a thread pool that creates new 
                                  threads as needed, but will reuse previously 
                                  constructed threads when they are available
newSingleThreadExecutor()         Creates a single thread. 

En el caso de un grupo de subprocesos fijos, si el ejecutor ejecuta actualmente todos los subprocesos, las tareas pendientes se colocan en una cola y se ejecutan cuando un subproceso queda inactivo.

Ejemplo de grupo de subprocesos

En el siguiente tutorial, veremos un ejemplo básico del ejecutor del grupo de subprocesos: FixedThreadPool.

Pasos a seguir

1. Create a task(Runnable Object) to execute
2. Create Executor Pool using Executors
3. Pass tasks to Executor Pool
4. Shutdown the Executor Pool
// Java program to illustrate 
// ThreadPool
import java.text.SimpleDateFormat; 
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
  
// Task class to be executed (Step 1)
class Task implements Runnable   
{
    private String name;
      
    public Task(String s)
    {
        name = s;
    }
      
    // Prints task name and sleeps for 1s
    // This Whole process is repeated 5 times
    public void run()
    {
        try
        {
            for (int i = 0; i<=5; i++)
            {
                if (i==0)
                {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("Initialization Time for"
                            + " task name - "+ name +" = " +ft.format(d));   
                    //prints the initialization time for every task 
                }
                else
                {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("Executing Time for task name - "+
                            name +" = " +ft.format(d));   
                    // prints the execution time for every task 
                }
                Thread.sleep(1000);
            }
            System.out.println(name+" complete");
        }
          
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
public class Test
{
     // Maximum number of threads in thread pool
    static final int MAX_T = 3;             
  
    public static void main(String[] args)
    {
        // creates five tasks
        Runnable r1 = new Task("task 1");
        Runnable r2 = new Task("task 2");
        Runnable r3 = new Task("task 3");
        Runnable r4 = new Task("task 4");
        Runnable r5 = new Task("task 5");      
          
        // creates a thread pool with MAX_T no. of 
        // threads as the fixed pool size(Step 2)
        ExecutorService pool = Executors.newFixedThreadPool(MAX_T);  
         
        // passes the Task objects to the pool to execute (Step 3)
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        pool.execute(r4);
        pool.execute(r5); 
          
        // pool shutdown ( Step 4)
        pool.shutdown();    
    }
}

Ejemplo de ejecución

Output:
Initialization Time for task name - task 2 = 02:32:56
Initialization Time for task name - task 1 = 02:32:56
Initialization Time for task name - task 3 = 02:32:56
Executing Time for task name - task 1 = 02:32:57
Executing Time for task name - task 2 = 02:32:57
Executing Time for task name - task 3 = 02:32:57
Executing Time for task name - task 1 = 02:32:58
Executing Time for task name - task 2 = 02:32:58
Executing Time for task name - task 3 = 02:32:58
Executing Time for task name - task 1 = 02:32:59
Executing Time for task name - task 2 = 02:32:59
Executing Time for task name - task 3 = 02:32:59
Executing Time for task name - task 1 = 02:33:00
Executing Time for task name - task 3 = 02:33:00
Executing Time for task name - task 2 = 02:33:00
Executing Time for task name - task 2 = 02:33:01
Executing Time for task name - task 1 = 02:33:01
Executing Time for task name - task 3 = 02:33:01
task 2 complete
task 1 complete
task 3 complete
Initialization Time for task name - task 5 = 02:33:02
Initialization Time for task name - task 4 = 02:33:02
Executing Time for task name - task 4 = 02:33:03
Executing Time for task name - task 5 = 02:33:03
Executing Time for task name - task 5 = 02:33:04
Executing Time for task name - task 4 = 02:33:04
Executing Time for task name - task 4 = 02:33:05
Executing Time for task name - task 5 = 02:33:05
Executing Time for task name - task 5 = 02:33:06
Executing Time for task name - task 4 = 02:33:06
Executing Time for task name - task 5 = 02:33:07
Executing Time for task name - task 4 = 02:33:07
task 5 complete
task 4 complete

Como se ve en la ejecución del programa, la tarea 4 o la tarea 5 se ejecutan solo cuando un subproceso en el grupo queda inactivo. Hasta entonces, las tareas adicionales se colocan en una cola.

TP Exec 1

Thread Pool ejecutando las tres primeras tareas

TP Exec 2

Thread Pool ejecutando tareas 4 y 5

One of the main advantages of using this approach is when you want to process 100 requests at a time, but do not want to create 100 Threads for the same, so as to reduce JVM overload. You can use this approach to create a ThreadPool of 10 Threads and you can submit 100 requests to this ThreadPool. 
ThreadPool will create maximum of 10 threads to process 10 requests at a time.  After process completion of any single Thread, 
ThreadPool will internally allocate the 11th request to this Thread 
and will keep on doing the same to all the remaining requests.

Riesgos en el uso de grupos de subprocesos

  1. Interbloqueo : si bien el interbloqueo puede ocurrir en cualquier programa de subprocesos múltiples, los grupos de subprocesos introducen otro caso de interbloqueo, uno en el que todos los subprocesos en ejecución esperan los resultados de los subprocesos bloqueados que esperan en la cola debido a la falta de disponibilidad de subprocesos para ejecución.
  2. Fuga de subprocesos: la fuga de subprocesos se produce si se elimina un subproceso del grupo para ejecutar una tarea, pero no se devuelve a él cuando se completa la tarea. Como ejemplo, si el subproceso lanza una excepción y la clase de grupo no detecta esta excepción, entonces el subproceso simplemente se cerrará, reduciendo el tamaño del grupo de subprocesos en uno. Si esto se repite muchas veces, el grupo finalmente se vaciaría y no habría subprocesos disponibles para ejecutar otras requests.
  3. Thrashing de recursos: si el tamaño del grupo de subprocesos es muy grande, se pierde tiempo en el cambio de contexto entre subprocesos. Tener más subprocesos que el número óptimo puede causar un problema de inanición que conduce a una paliza de recursos como se explica.

Puntos importantes

  1. No ponga en cola tareas que esperan simultáneamente los resultados de otras tareas. Esto puede conducir a una situación de interbloqueo como se describe anteriormente.
  2. Tenga cuidado al usar subprocesos para una operación de larga duración. Podría dar como resultado que el subproceso esperara eternamente y eventualmente provocaría una fuga de recursos.
  3. El grupo de subprocesos debe terminarse explícitamente al final. Si esto no se hace, entonces el programa continúa ejecutándose y nunca termina. Llame a shutdown() en el grupo para finalizar el ejecutor. Si intenta enviar otra tarea al ejecutor después del apagado, generará una RejectedExecutionException.
  4. Uno necesita comprender las tareas para ajustar efectivamente el grupo de subprocesos. Si las tareas son muy contrastantes, tiene sentido usar diferentes grupos de subprocesos para diferentes tipos de tareas a fin de ajustarlas correctamente.
  5. Puede restringir la cantidad máxima de subprocesos que se pueden ejecutar en JVM, lo que reduce las posibilidades de que JVM se quede sin memoria.
  6. Si necesita implementar su ciclo para crear nuevos subprocesos para el procesamiento, el uso de ThreadPool ayudará a procesar más rápido, ya que ThreadPool no crea nuevos subprocesos después de alcanzar su límite máximo.
  7. Después de completar el procesamiento de subprocesos, ThreadPool puede usar el mismo subproceso para realizar otro proceso (ahorrando así el tiempo y los recursos para crear otro subproceso).

Grupo de hilos de sintonización

  • El tamaño óptimo del grupo de subprocesos depende de la cantidad de procesadores disponibles y la naturaleza de las tareas. En un sistema de procesador N para una cola de solo procesos de tipo computación, un tamaño máximo de grupo de subprocesos de N o N+1 logrará la máxima eficiencia. de tiempo de espera (W) y tiempo de servicio (S) para una solicitud; lo que da como resultado un tamaño máximo de grupo de N*(1+ W/S) para lograr la máxima eficiencia.

El grupo de subprocesos es una herramienta útil para organizar aplicaciones de servidor. Es bastante sencillo en concepto, pero hay varios problemas a tener en cuenta al implementar y usar uno, como interbloqueo, hiperpaginación de recursos. El uso del servicio ejecutor hace que sea más fácil de implementar.

Este artículo es una contribución de Abhishek . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *