Programación concurrente de Java: qué grupo de subprocesos se utiliza en la producción real (principio del grupo de subprocesos ThreadPoolExecutor y preguntas de la entrevista)

problema

¿Dónde están las ventajas y desventajas de los grupos de subprocesos?
¿Cuál es el principio de implementación subyacente del grupo de subprocesos?
¿Cómo hacer un buen uso del grupo de subprocesos en el desarrollo diario?
¿Qué grupo de subprocesos se utiliza realmente?

Grupo de subprocesos ThreadPoolExecutor

Concepto
El trabajo realizado por el grupo de subprocesos es principalmente para controlar la cantidad de subprocesos en ejecución. Durante el procesamiento, las tareas se agregan a la cola , y luego estas tareas se inician después de que se crea el subproceso. Si se excede el número máximo , el exceso de subprocesos esperará en línea y esperará otros subprocesos. Una vez completada la ejecución, la tarea se saca de la cola para su ejecución.

Ventajas
Sus principales características son: reutilización de hilos, controlar el número máximo de concurrencia y gestionar hilos.

  1. Reutilización de subprocesos: no es necesario conservar nuevos subprocesos, reutilizar subprocesos que se han creado para reducir la sobrecarga de creación y destrucción de subprocesos y ahorrar recursos del sistema.
  2. Mejorar la velocidad de respuesta: cuando se alcanza la tarea, no es necesario crear un nuevo hilo, utilice directamente el hilo del grupo de hilos.
  3. Gestión de subprocesos: puedes controlar el número máximo de subprocesos simultáneos, controlar la creación de subprocesos, etc.

Sistema
ExecutorExecutorServiceAbstractExecutorServiceThreadPoolExecutor. ThreadPoolExecutorEs la clase principal creada por el grupo de subprocesos. Al igual Arraysque las Collectionsherramientas, Executortambién tienen sus propias herramientas Executors.

Implementación de arquitectura

El grupo de subprocesos en Java se implementa a través del marco Executor, que utiliza las clases Executor, Executors, ExecutorService y ThreadPoolExecutor.
Inserte la descripción de la imagen aquí

Cómo usar el grupo de subprocesos

Tres formas comunes de crear grupos de subprocesos

newFixedThreadPool : LinkedBlockingQueueimplementación de uso , grupo de subprocesos de longitud fija.

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

newSingleThreadExecutor : LinkedBlockingQueueImplementación de uso , un grupo tiene solo un hilo.

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

newCachedThreadPool : SynchronousQueueImplementación de uso , grupo de subprocesos de longitud variable.

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());

Siete parámetros del grupo de subprocesos

int corePoolSize,核心线程
int maximumPoolSize,非核心线程
long keepAliveTime,时间
TimeUnit unit,时间单位
BlockingQueue<Runnable> workQueue,队列
 ThreadFactory threadFactory,线程工厂
RejectedExecutionHandler handler 拒绝策略

formar

parámetro significado
corePoolSize Número de subprocesos principales residentes en el grupo de subprocesos
maximumPoolSize Número máximo de subprocesos que se pueden acomodar
keepAliveTime Tiempo de supervivencia del hilo inactivo
unidad Unidad de tiempo de supervivencia
workQueue Una cola que almacena tareas enviadas pero no ejecutadas
threadFactory Clase de fábrica para crear hilos
manipulador Política de rechazo después de que la cola de espera esté llena

Notas de método

corePoolSize: el número de subprocesos que se mantendrán en el grupo, incluso si están inactivos, a menos que allowCoreThreadTimeOut esté establecido como
maximumPoolSize: el número máximo de subprocesos que se permitirán en el grupo
keepAliveTime: cuando el número de subprocesos es mayor que el núcleo, este es el tiempo máximo que el exceso de subprocesos inactivos esperará nuevas tareas antes de terminar.
unit: la unidad de tiempo para el argumento
keepAliveTime workQueue: la cola que se usa para mantener las tareas antes de que se ejecuten. Esta cola contendrá solo las tareas ejecutables enviadas por el método de ejecución.
threadFactory: la fábrica que se usa cuando el ejecutor crea un nuevo
controlador de subprocesos : el controlador que se usa cuando la ejecución está bloqueada porque se alcanzan los límites del subproceso y las capacidades de la cola

Código

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    
    
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Comprensión : los parámetros de creación del grupo de subprocesos son como un banco .

corePoolSizeAl igual que la " ventanilla de servicio " del banco , por ejemplo, hoy en día hay 2 cajeros que atienden las solicitudes (tareas) de los clientes . Si hay más de 2 clientes, los nuevos clientes esperarán en el área de espera (cola de espera workQueue). Cuando el área de espera está llena, la " ventana de horas extras " debe abrirse en este momento para permitir que los otros 3 cajeros trabajen horas extras. En este momento, la ventana máximamaximumPoolSize es de 5. Si todas las ventanas están abiertas y la zona de espera aún está llena, se debe activar la " estrategia de rechazo " en este momento para handlerdecirle a la afluencia de clientes que no ingresen, que está llena. Dado que no hay más afluencia de nuevos clientes, se terminan más clientes y la ventana se vuelve inactiva. En este momento, al keepAlivetTimecancelar las 3 "ventanas de tiempo extra" adicionales, restaure a 2 "ventanas de servicio".

Proceso

Si el número de subprocesos en ejecución es menor que corePoolSize, cree un subproceso central; si es mayor o igual que corePoolSize, colóquelo en la cola de espera.

Si la cola de espera está llena, pero el número de subprocesos en ejecución es menor que maximumPoolSize, cree un subproceso no central; si es mayor o igual que maximumPoolSize, inicie la estrategia de rechazo.

Cuando un subproceso no tiene nada que hacer durante un período de keepAliveTime, si el número de subprocesos en ejecución es mayor que corePoolSize, se cierra el subproceso no principal.

Estrategia de rechazo del grupo de subprocesos

Inserte la descripción de la imagen aquí

Cuando la cola de espera está llena, se alcanza el número máximo de subprocesos y llega una nueva tarea, es necesario iniciar la estrategia de rechazo. JDK proporciona cuatro estrategias de rechazo, respectivamente.

  1. AbortPolicy : la política predeterminada, que lanza directamente RejectedExecutionExceptionuna excepción para evitar que el sistema se ejecute normalmente.
  2. CallerRunsPolicy : no lanza una excepción ni finaliza la tarea, pero devuelve la tarea a la persona que llama.
  3. DiscardOldestPolicy : descarte la tarea de espera más larga en la cola y luego agregue la tarea actual a la cola e intente enviar la tarea nuevamente.
  4. DiscardPolicy : descarte la tarea directamente sin ningún procesamiento.

¿Qué grupo de subprocesos se utiliza en la producción real (énfasis)?

Imagen de la versión de Songshan del manual de desarrollo de Ali Java
Inserte la descripción de la imagen aquí

¡No se necesitan longitudes únicas, variables y fijas ! La razón es que, FixedThreadPooly SingleThreadExecutorse usa la capa inferior LinkedBlockingQueue, se logra la longitud máxima de la cola Integer.MAX_VALUE, obviamente conduce a OOM. Por lo tanto, en la producción real, generalmente pasa ThreadPoolExecutor7 parámetros y personaliza el grupo de subprocesos.

ExecutorService threadPool=new ThreadPoolExecutor(2,5,
                        1L,TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());

Selección de parámetros de grupo de subprocesos personalizados

Tareas intensivas en CPU

Para tareas con uso intensivo de CPU, el número máximo de subprocesos es el número de subprocesos de CPU + 1.

El uso intensivo de CPU significa que la tarea requiere muchos cálculos sin bloqueo. La CPU ha estado funcionando a toda velocidad.
Las tareas intensivas de CPU solo se pueden acelerar en una verdadera
CPU de varios núcleos (a través de múltiples subprocesos) y en una CPU de un solo núcleo (¿tragedia? ), no importa cuántos subprocesos múltiples simulados abra, la tarea no se puede acelerar, porque la potencia informática total de la CPU es solo eso.
Las tareas intensivas en CPU configuran la menor cantidad de subprocesos posible:
fórmula general: número de núcleos de CPU + 1 grupo de subprocesos

Tareas intensivas en IO

Para tareas intensivas en E / S, asigne tantos puntos como sea posible, que puede ser el número de subprocesos de CPU * 2 o el número de subprocesos de CPU / (factor de bloqueo 1).

Experiencia de una gran fábrica

Intensivo en IO, es decir, la tarea requiere mucho O, es decir, mucho bloqueo.
La ejecución de tareas con un uso intensivo de 10 en un solo subproceso dará como resultado el desperdicio de una gran cantidad de potencia informática de la CPU desperdiciada en la espera.
Por lo tanto, el uso de subprocesos múltiples en tareas con un uso intensivo de IO puede acelerar enormemente la ejecución del programa. Incluso en una CPU de un solo núcleo, esta aceleración es principalmente Utilice el tiempo de bloqueo perdido.
Cuando IO es intensivo, la mayoría de los subprocesos están bloqueados, por lo que debe configurar el número de subprocesos.
Fórmula de referencia: número de núcleo de CPU / coeficiente de bloqueo 1. El
coeficiente de bloqueo está entre 0,8 y 09. Por
ejemplo, CPU de 8 núcleos: 8 /(1-0.9)=80 Hilos

Hablar sobre el principio de funcionamiento subyacente del grupo de subprocesos (énfasis)

Inserte la descripción de la imagen aquí

El siguiente contenido es muy importante, el siguiente contenido es muy importante, el siguiente contenido es muy importante

1. Después de crear el grupo de subprocesos, espere la solicitud de tarea enviada.
2. Cuando se llama al método execute (para agregar una tarea de solicitud, el grupo de subprocesos hará los siguientes juicios
 2.1 Si el número de subprocesos en ejecución es menor que corePoolSize, entonces cree inmediatamente un subproceso para ejecutar la tarea;
 2.2 Si el número de subprocesos en ejecución es mayor o igual que corePoolSize, entonces Ponga esta tarea en la cola
 2.3 Si la cola está llena en este momento y la cantidad de subprocesos en ejecución es menor que maximumPoolSize, entonces todavía tiene que crear subprocesos no centrales para ejecutar esta tarea inmediatamente
 2.4 Si la cola está llena y la cantidad de subprocesos en ejecución es mayor o igual a maximumPoolSize , Luego, el grupo de subprocesos iniciará la estrategia de rechazo de saturación para ejecutarse .
3. Cuando un subproceso completa la tarea, tomará la siguiente tarea de la cola para su ejecución.
4. Cuando un subproceso no tiene nada que hacer durante más de cierto tiempo (keepAlive Time) En ese momento, el grupo de subprocesos juzgará:
si el número de subprocesos actualmente en ejecución es mayor que corePoolSize, este subproceso se detendrá.
Por lo tanto, después de que se completen todas las tareas del grupo de subprocesos, eventualmente se reducirá al tamaño de corePoolsize .

Cómo verificar la cantidad de núcleos de CPU

Botón derecho del ratón, ¡¡¡No !!!

System.out.println(Runtime.getRuntime().availableProcessors());

Referencia

Grupo de subprocesos de programación concurrente de Java ThreadPoolExecutor utiliza
Ali boss para que comprenda el principio subyacente del grupo de subprocesos
JVM-JUC-Core
Ali manual de desarrollo de Java Songshan version.pdf

Supongo que te gusta

Origin blog.csdn.net/e891377/article/details/108737161
Recomendado
Clasificación