Principio de implementación del grupo de subprocesos de Java y análisis del código fuente

Principio de implementación del grupo de subprocesos de Java y análisis del código fuente


prefacio

Este artículo comenzó a escribirse a fines de noviembre de 2019, se retrasó hasta fines de 2020 y no se terminó hasta principios de 2021.

¡El tiempo es demasiado rápido y demasiado lento~!

Recuerdo vagamente que en octubre de 2019, cuando un tal Dong dejó la empresa de nueva creación y planeó una entrevista para un trabajo, me preguntó al grupo de subprocesos, ¿lo sabrías? Luego me envió una nota que escribí en 2017 "Puntos de conocimiento esenciales del conjunto de subprocesos de programación concurrente de Java", dijo, ¿eso es todo? ¡En ese momento, pensé que había casi tantos grupos de subprocesos ~!

El 9 de noviembre de 2019, un tal Dong y yo tomamos el autobús 815 de Dawang Road a Yanjiao. En ese momento, fue solo porque estaba aprendiendo algunos puntos de conocimiento relacionados con subprocesos múltiples, y charlamos en el autobús porque no había nada que hacer. En ese momento, me hizo algunas preguntas sobre el grupo de subprocesos.Creo que en el grupo de subprocesos de trabajo habitual, si sabe cómo usarlo, puede optimizar la cantidad de subprocesos principales como máximo. La discusión principal está relacionada con la concurrencia y los bloqueos de subprocesos múltiples.

Al final del año, generalmente estaba ocupado en el trabajo, por lo que rara vez estudiaba por mi cuenta. Después de una semana, recordé la pregunta de Dong Dong: "¿Cómo se generan los subprocesos en el grupo de subprocesos y cómo se ejecutan las tareas?". ¿esperar la ejecución?".

No tengo muy claro la lógica de esta pieza, así que grabé temporalmente este elemento TODO, y creo que lo estudiaré cuando tenga tiempo. Como resultado, esto abarcó 2019 y 2020 y llegó directamente a 2021.

Perdone mi extensión, el punto clave es que el lapso de tiempo de este artículo es demasiado largo y me ha dejado una profunda impresión. ¡Tengo que hablar de eso, empecemos a ponernos manos a la obra~!

JDK1.8El código fuente para analizar el diseño central y la implementación del conjunto de subprocesos de Java.

Este artículo se refiere al principio de implementación del conjunto de subprocesos de Java y su práctica en el negocio de Meituan .

El principio de implementación del grupo de subprocesos de Java y su práctica en el negocio de Meituan. Este artículo está muy bien escrito. Además del contenido de este artículo, este artículo también describe los antecedentes del grupo de subprocesos , la práctica y la dinámica del grupo de subprocesos en los negocios . Por lo tanto, Si desea conocer este tipo de grupos de subprocesos, puede leer el artículo sobre el principio de implementación de los grupos de subprocesos de Java y su práctica en el negocio de Meituan .

Si el lector es un estudiante que está realizando desarrollo del lado del servidor, se recomienda encarecidamente leer el principio de implementación del conjunto de subprocesos de Java y su práctica en el negocio de Meituan .

Exterior

La apariencia son principalmente algunos puntos que solemos ver cuando usamos el grupo de subprocesos.

  • relación de herencia;
  • Constructor;
  • parámetros en el constructor;
  • Cola de bloqueo en el constructor;
  • Creación de grupo de subprocesos;
  • Política de denegación en el constructor;

herencia del grupo de subprocesos

ThreadPoolExecutor-uml.png

ThreadPoolExecutorLa interfaz de nivel superior implementada es que Executoren la interfaz, Executorlos usuarios no necesitan prestar atención a cómo crear subprocesos y cómo programar subprocesos para ejecutar tareas. Los usuarios solo necesitan proporcionar Runnableobjetos, enviar la lógica de operación de la tarea al ejecutor Executory Executorel marco completa la implementación de subprocesos y la parte de ejecución de tareas.

ExecutorServiceLa interfaz agrega algunas capacidades:

  1. Ampliar la capacidad de ejecutar tareas, complementando Futurelos métodos que se pueden generar para una o un lote de tareas asincrónicas;
  2. Proporciona métodos para administrar y controlar el grupo de subprocesos, como detener la operación del grupo de subprocesos.

AbstractExecutorServiceEs la clase abstracta de la capa superior, que conecta el proceso de ejecución de tareas en serie, lo que garantiza que la implementación de la capa inferior solo necesite centrarse en un método de ejecución de tareas.

La clase de implementación de nivel más bajo ThreadPoolExecutorimplementa la parte de operación más compleja:

  1. Se puede crear, administrar y reutilizar automáticamente un conjunto de un número específico de subprocesos, y la parte correspondiente solo necesita enviar la tarea

  2. Seguridad de subprocesos, ThreadPoolExecutorestado interno, número de subprocesos centrales, subprocesos no centrales y otros atributos, uso extensivo de mecanismos de bloqueo CAS y AQS para evitar conflictos causados ​​por la concurrencia

  3. Proporciona los conceptos de subprocesos centrales, colas de bloqueo de búfer, subprocesos no centrales y estrategias de descarte, que se pueden combinar y usar de acuerdo con los escenarios de aplicación reales.

  4. Proporciona beforeExecutey afterExecute()puede admitir la extensión de la función del grupo de subprocesos

Constructor

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;
}
  • corePoolSize : la cantidad de subprocesos principales en el grupo de subprocesos. En general, no importa si hay tareas o no, siempre sobrevivirán en el grupo de subprocesos. Solo cuando ThreadPoolExecutorel subprocesosallowCoreThreadTimeOut(boolean value) principales inactivos tendrán un mecanismo de tiempo de espera. Si no hay nuevas tareas dentro del tiempo especificado trueCuando llega, el subproceso principal también se terminará, y este intervalo de tiempo keepAliveTimese especifica .
  • MaximumPoolSize : la cantidad máxima de subprocesos que puede acomodar el grupo de subprocesos. Cuando la cantidad de subprocesos activos alcanza este valor, las tareas nuevas posteriores se bloquearán.
  • keepAliveTime : Controla el período de tiempo de espera cuando el subproceso está inactivo. Si excede, el subproceso se terminará. Generalmente se usa para subprocesos no centrales, ThreadPoolExecutorsolo cuandoallowCoreThreadTimeOut(boolean value) el método está configurado en , también actúa en subprocesos principales.true
  • unit : se usa para especificar keepAliveTimela unidad de tiempo del parámetro, TimeUnites un enumtipo de enumeración, comúnmente se usan: TimeUnit.HOURS(小时), TimeUnit.MINUTES(分钟), TimeUnit.SECONDS(秒) y TimeUnit.MILLISECONDS(毫秒)etc.
  • workQueue : la cola de tareas del grupo de subprocesos, execute(Runnable command)la tarea se Runnablealmacenará en la cola a través del método del grupo de subprocesos.
  • threadFactory : fábrica de subprocesos, que es una interfaz utilizada para crear nuevos subprocesos para el grupo de subprocesos.
  • handler : Estrategia de rechazo.La llamada estrategia de rechazo se refiere a la estrategia correspondiente adoptada por el grupo de subprocesos para rechazar la tarea cuando la tarea se agrega al grupo de subprocesos.

Variables miembro

/**
 * 任务阻塞队列 
 */
private final BlockingQueue<Runnable> workQueue; 
/**
 * 非公平的互斥锁(可重入锁)
 */
private final ReentrantLock mainLock = new ReentrantLock();
/**
 * 线程集合一个Worker对应一个线程,没有核心线程的说话,只有核心线程数
 */
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
 * 配合mainLock通过Condition能够更加精细的控制多线程的休眠与唤醒
 */
private final Condition termination = mainLock.newCondition();
/**
 * 线程池中线程数量曾经达到过的最大值。
 */
private int largestPoolSize;  
/**
 * 已完成任务数量
 */
private long completedTaskCount;
/**
 * ThreadFactory对象,用于创建线程。
 */
private volatile ThreadFactory threadFactory;  
/**
 * 拒绝策略的处理句柄
 * 现在默认提供了CallerRunsPolicy、AbortPolicy、DiscardOldestPolicy、DiscardPolicy
 */
private volatile RejectedExecutionHandler handler;
/**
 * 线程池维护线程(超过核心线程数)所允许的空闲时间
 */
private volatile long keepAliveTime;
/**
 * 允许线程池中的核心线程超时进行销毁
 */
private volatile boolean allowCoreThreadTimeOut;  
/**
 * 线程池维护线程的最小数量,哪怕是空闲的  
 */
private volatile int corePoolSize;
/**
 * 线程池维护的最大线程数量,线程数超过这个数量之后新提交的任务就需要进入阻塞队列
 */
private volatile int maximumPoolSize;

Crear un grupo de subprocesos

ExecutorsProporciona métodos para obtener varios grupos de subprocesos de uso común:

  • Grupo de subprocesos de caché

newCachedThreadPoolEs un grupo de subprocesos que crea nuevos subprocesos según sea necesario, pero reutiliza subprocesos construidos previamente a medida que están disponibles. Para los programas que realizan muchas tareas asincrónicas de corta duración, estos grupos de subprocesos a menudo mejoran el rendimiento del programa. La execute()llamada reutilizará un subproceso construido previamente (si hay un subproceso disponible). Si no hay ningún subproceso disponible, se crea un nuevo subproceso y se agrega al grupo. Termina y elimina los subprocesos que no se han utilizado durante 60 segundos de la memoria caché. Por lo tanto, un grupo de subprocesos que permanece inactivo durante mucho tiempo no utilizará ningún recurso. Tenga en cuenta que los grupos de subprocesos con propiedades similares pero diferentes detalles (como los parámetros de tiempo de espera) se pueden crear utilizando el ThreadPoolExecutorconstructor .

public static ExecutorService newCachedThreadPool() {
    
    
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                  60L, TimeUnit.SECONDS,
                  new SynchronousQueue<Runnable>());
}
  • grupo de subprocesos de un solo subproceso

newSingleThreadExecutorCree un grupo de un solo subproceso, es decir, el grupo de subprocesos tiene solo un subproceso en funcionamiento y todas las tareas se ejecutan en serie. Si el único subproceso finaliza de manera anormal, un nuevo subproceso lo reemplazará. Este grupo de subprocesos Asegúrese de que el orden de ejecución de todos tareas se ejecuta en el orden en que se envían las tareas.

public static ExecutorService newSingleThreadExecutor() {
    
    
  return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>()));
}
  • grupo de subprocesos de tamaño fijo

newFixedThreadPoolCree un grupo de subprocesos de tamaño fijo y cree un subproceso cada vez que se envíe una tarea hasta que el subproceso alcance el tamaño máximo del grupo de subprocesos. Una vez que el grupo de subprocesos alcanza el tamaño máximo, permanecerá sin cambios. Si un subproceso finaliza debido a una ejecución anormal, entonces el grupo de subprocesos agregará un nuevo subproceso.

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    
    
  return new ThreadPoolExecutor(nThreads, nThreads,
                  0L, TimeUnit.MILLISECONDS,
                  new LinkedBlockingQueue<Runnable>(),
                  threadFactory);
}
  • grupo de subprocesos de un solo subproceso

newScheduledThreadPoolCree un grupo de subprocesos de tamaño ilimitado que admita el tiempo y la ejecución periódica de tareas.

public static ScheduledExecutorService newScheduledThreadPool(
    int corePoolSize, ThreadFactory threadFactory) {
  return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                   ThreadFactory threadFactory) {
  super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
      new DelayedWorkQueue(), threadFactory);
}

Podemos ver que el método anterior usa un total de DelayedWorkQueue, LinkedBlockingQueuey SynchronousQueue. Esta es la cola de bloqueo, uno de los núcleos de subprocesos.

cola de bloqueo de tareas

BlockingQueue.png

Generalmente se divide en colas de envío directo, colas de tareas limitadas, colas de tareas ilimitadas y colas de tareas prioritarias;

SynchronousQueue

1. Cola de envío directo : configurada como SynchronousQueueuna cola, SynchronousQueuees especial BlockingQueue, no tiene capacidad, se bloqueará cada vez que se realice una operación de inserción y se activará solo después de que se realice otra operación de eliminación; de lo contrario, cada eliminación La operación debe esperar a la operación de inserción correspondiente.

Usando SynchronousQueuela cola, las tareas enviadas no se guardarán y siempre se enviarán para su ejecución inmediata. Si el número de subprocesos utilizados para ejecutar la tarea es inferior a maximumPoolSize, intente crear un nuevo proceso, si alcanza maximumPoolSizeel valor máximo establecido, handlerrechazará la ejecución de acuerdo con la política que establezca. Por lo tanto, las tareas que envíe de esta manera no se almacenarán en caché, sino que se ejecutarán de inmediato. En este caso, debe tener una evaluación precisa de la concurrencia de su programa antes de poder establecer el número apropiado; de lo contrario, es fácil maximumPoolSize. se implementará una política de rechazo;

ArrayBlockingQueue

2. Cola de tareas limitada : una cola de tareas limitada se puede implementar ArrayBlockingQueuede la siguiente manera:

pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

Usando ArrayBlockingQueueuna cola de tareas limitada, si es necesario ejecutar una nueva tarea, el grupo de subprocesos creará un nuevo subproceso, hasta que el número de subprocesos creados alcance corePoolSize, la nueva tarea se agregará a la cola de espera. Si la cola de espera está llena, es decir, excede ArrayBlockingQueuela capacidad inicializada, continúe creando hilos hasta que el número de hilos alcance el maximumPoolSizenúmero máximo de hilos establecido, y si es mayor maximumPoolSize, ejecute la estrategia de rechazo. En este caso, el límite superior del número de subprocesos está directamente relacionado con el estado de la cola de tareas delimitada.Si la capacidad inicial de la cola delimitada es grande o no se alcanza el estado de sobrecarga, el número de subprocesos siempre será se mantiene debajo de E. De lo contrario, cuando la corePoolSizcola de tareas esté llena, se maximumPoolSizelimitará .

LinkedBlockingQueue

3. Cola de tareas ilimitada : la cola de tareas ilimitada se puede implementar LinkedBlockingQueuede la siguiente manera:

pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

Usando una cola de tareas ilimitada, la cola de tareas del grupo de subprocesos puede agregar nuevas tareas sin límite, y la cantidad máxima de subprocesos creados por el grupo de subprocesos es el número que establezca, es decir, este parámetro no es válido en este corePoolSizecaso maximumPoolSize, incluso si hay muchas tareas no ejecutadas almacenadas en caché en la cola de tareas. Cuando la cantidad de subprocesos en el grupo de subprocesos alcance corePoolSize, no aumentará más; si se agregan nuevas tareas más tarde, ingresarán directamente a la cola y esperarán. Cuando se usa este modo de cola de tareas, asegúrese de prestar atención a la coordinación y el control entre el envío y el procesamiento de su tarea, de lo contrario, habrá un problema de que las tareas en la cola seguirán creciendo debido a la incapacidad de procesarlas a tiempo hasta que los recursos están agotados.

PriorityBlockingQueue

4. Cola de tareas prioritarias : la cola de tareas prioritarias se PriorityBlockingQueueimplementa mediante:

Las tareas se reorganizarán y ejecutarán según la prioridad, y la cantidad de subprocesos en el grupo de subprocesos siempre es corePoolSize, es decir, solo hay uno.

PriorityBlockingQueueDe hecho, es una cola ilimitada especial. No importa cuántas tareas se le agreguen, la cantidad de subprocesos creados por el grupo de subprocesos no excederá el número máximo corePoolSize, pero otras colas generalmente procesan las tareas de acuerdo con el primero en primer lugar. -out regla, y PriorityBlockingQueuela cola se puede personalizar Las reglas se ejecutan secuencialmente de acuerdo con el orden de prioridad de las tareas.

De hecho, LinkedBlockingQueueel límite también se puede establecer y su límite predeterminado es Integer.MAX_VALUE. Al mismo tiempo, también admite establecer el tamaño de la cola al construir.

política de rechazo

public interface RejectedExecutionHandler {
    
    
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

Cuando Executorse ha cerrado, es decir, después de que se ha ejecutado el método executorService.shutdown(), o Executorcuando se utilizan límites acotados para la capacidad máxima de subprocesos y colas de trabajo, y se han saturado. execute()Las nuevas tareas enviadas con el método serán rechazadas.
En el caso anterior, executeel método llamará a su métodoRejectedExecutionHandler .RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)

Política de rechazo predeterminada de AbortPolicy

También conocida como política de finalización, un rechazo lanza el tiempo de ejecución RejectedExecutionException. El lado comercial puede obtener comentarios oportunos sobre los resultados enviados para esta tarea al detectar excepciones.

public static class AbortPolicy implements RejectedExecutionHandler {
    
    
  public AbortPolicy() {
    
     }
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
    throw new RejectedExecutionException("Task " + r.toString() +
                                         " rejected from " +
                                         e.toString());
  }
}

CallerRunsPolicy

Tener un control de retroalimentación autónomo que permita a los remitentes realizar tareas de envío puede ralentizar el envío de nuevas tareas. En este caso, es necesario dejar que se ejecuten todas las tareas.

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    
    
    public CallerRunsPolicy() {
    
     }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
        if (!e.isShutdown()) {
    
    
            r.run();
        }
    }
}

DiscardPolicy

Un controlador para rechazar tareas, descartando silenciosamente tareas. Usando esta estrategia, es posible que no podamos percibir el estado anormal del sistema. ¡Usar con precaución~!

public static class DiscardPolicy implements RejectedExecutionHandler {
    
    
    public DiscardPolicy() {
    
     }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
    }
}

DiscardOldestPolicy

Deseche la tarea que se encuentra más adelante en la cola y vuelva a enviar las tareas rechazadas. El uso de esta estrategia depende de si el negocio debe ser reemplazado por lo nuevo y lo viejo, ¡así que úselo con precaución ~!

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    
    
    public DiscardOldestPolicy() {
    
     }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
        if (!e.isShutdown()) {
    
    
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

núcleo

Hablé sobre la apariencia del grupo de subprocesos antes , y luego hablaré sobre su núcleo .

El grupo de subprocesos en realidad crea un modelo de productor-consumidor internamente , que se desacoplará 线程de 任务los dos y no estará directamente relacionado, a fin de almacenar bien las tareas y reutilizar los subprocesos.

El funcionamiento del grupo de subprocesos se divide principalmente en dos partes: gestión de tareas y gestión de subprocesos.

La parte de administración de tareas actúa como productor. Cuando se envía una tarea, el grupo de subprocesos juzgará el flujo posterior de la tarea:

  1. Solicite directamente que el subproceso realice la tarea;
  2. Buffer en la cola y esperar a que se ejecute el subproceso;
  3. Rechazar la tarea.

La parte de gestión de subprocesos es el consumidor, que se mantiene uniformemente en el grupo de subprocesos, y el subproceso se asigna de acuerdo con la solicitud de la tarea. Después de que el subproceso ejecuta la tarea, continuará obteniendo nuevas tareas para ejecutar. Finalmente, cuando el subproceso no puede obtener la tarea, el hilo será reciclado.

A continuación, explicaremos en detalle el mecanismo de operación del grupo de subprocesos de acuerdo con las siguientes tres partes:

  1. Cómo el grupo de subprocesos mantiene su propio estado.
  2. Cómo gestiona las tareas el grupo de subprocesos.
  3. Cómo el grupo de subprocesos administra los subprocesos.

El ciclo de vida del grupo de subprocesos

El usuario no establece explícitamente el estado de ejecución del grupo de subprocesos, pero se mantiene internamente junto con la ejecución del grupo de subprocesos.

El grupo de subprocesos utiliza internamente una variable para mantener dos valores: el estado de ejecución ( runState) y el número de subprocesos ( workerCount).

En la implementación específica, el grupo de subprocesos reúne el mantenimiento de dos parámetros clave, el estado de ejecución ( runState) y la cantidad de subprocesos ( ):workerCount

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctlEste AtomicIntegertipo es un campo que controla el estado de ejecución del grupo de subprocesos y el número de subprocesos válidos en el grupo de subprocesos.

Contiene dos partes de información al mismo tiempo: el estado de ejecución del grupo de subprocesos ( runState) y el número de subprocesos efectivos en el grupo de subprocesos ( workerCount), los 3 bits superiores se guardan runStatey los 29 bits inferiores se guardan workerCounty el dos variables no interfieren entre sí.

Usar una variable para almacenar dos valores puede evitar inconsistencias al tomar decisiones relevantes, y no es necesario ocupar recursos de bloqueo para mantener la consistencia entre los dos. También se puede encontrar al leer el código fuente del grupo de subprocesos que a menudo es necesario juzgar el estado de ejecución del grupo de subprocesos y la cantidad de subprocesos al mismo tiempo. El grupo de subprocesos también proporciona varios métodos para que el usuario obtenga el estado de ejecución actual y el número de subprocesos del grupo de subprocesos. Aquí se usa el método de operación de bits, que es mucho más rápido que la operación básica (PD: este uso se puede ver en muchos códigos fuente).

El método de cálculo para obtener el estado del ciclo de vida y obtener el número de subprocesos del grupo de subprocesos en el paquete interno se muestra en el siguiente código:

private static final int COUNT_BITS = Integer.SIZE - 3;//32-3
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;//低29位都为1,高位都为0

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;//111
private static final int SHUTDOWN   =  0 << COUNT_BITS;//000
private static final int STOP       =  1 << COUNT_BITS;//001
private static final int TIDYING    =  2 << COUNT_BITS;//010
private static final int TERMINATED =  3 << COUNT_BITS;//011

// Packing and unpacking ctl
//计算当前运行状态,取高三位
private static int runStateOf(int c)     {
    
     return c & ~CAPACITY; }
//计算当前线程数量,取低29位
private static int workerCountOf(int c)  {
    
     return c & CAPACITY; }
//通过状态和线程数生成ctl
private static int ctlOf(int rs, int wc) {
    
     return rs | wc; }

ThreadPoolExecutorHay 5 estados de ejecución, a saber:

Estado operativo Descripción de la Situación
CORRER Puede aceptar tareas enviadas recientemente y también puede procesar tareas en la cola de bloqueo
CERRAR No puede aceptar tareas enviadas recientemente, pero puede continuar procesando tareas en la cola de bloqueo
DETENER No puede aceptar nuevas tareas, ni puede procesar tareas en la cola mientras interrumpe el proceso de la tarea.
ORDENAR Se terminaron todas las tareas, workCount (número de subprocesos efectivos) es 0
TERMINADO Ingrese a este estado después de que se ejecute el método terminado

Ciclo de declaración de grupo de subprocesos.jpg

Mecanismo de programación de tareas

La programación de tareas es el punto de entrada principal del grupo de subprocesos. Cuando un usuario envía una tarea, esta etapa determina cómo se ejecutará a continuación. Comprender esta parte es equivalente a comprender el mecanismo operativo central del grupo de subprocesos.

En primer lugar, executeel método completa la programación de todas las tareas. El trabajo realizado en esta parte es: verificar el estado de ejecución actual del grupo de subprocesos , el número de subprocesos en ejecución y la estrategia de ejecución , y determinar el próximo proceso de ejecución. , ya sea para aplicar directamente para la ejecución del subproceso , o Buffered a la cola para la ejecución , o rechazar directamente la tarea . Su proceso de ejecución es el siguiente:

  1. Primero verifique el estado de ejecución del grupo de subprocesos; si no RUNNING, rechácelo directamente, y el grupo de subprocesos debe asegurarse de que RUNNINGla tarea se ejecute en un estado determinado.
  2. Si workerCount < corePoolSize, se crea un subproceso y se inicia para ejecutar la tarea recién enviada.
  3. Si workerCount >= corePoolSizey la cola de bloqueo en el grupo de subprocesos no está llena, agregue la tarea a la cola de bloqueo.
  4. Si workerCount >= corePoolSize && workerCount < maximumPoolSizey la cola de bloqueo en el grupo de subprocesos está llena, cree e inicie un subproceso para ejecutar la tarea recién enviada.
  5. Si workerCount >= maximumPoolSizey la cola de bloqueo en el grupo de subprocesos está llena, la tarea se procesará de acuerdo con la estrategia de rechazo y el método de procesamiento predeterminado es lanzar una excepción directamente.

Diagrama de flujo de programación de tareas.png

Luego, ingrese el tiempo de análisis del código fuente ~!

enviar tarea

//AbstractExecutorService.java
public <T> Future<T> submit(Callable<T> task) {
    
    
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}
//ThreadPoolExecutor.java
public void execute(Runnable command) {
    
    
  if (command == null)
    throw new NullPointerException();
  int c = ctl.get();//获取ctl
  //检查当前核心线程数,是否小于核心线程数的大小限制
  if (workerCountOf(c) < corePoolSize) {
    
    
    //没有达到核心线程数的大小限制,那么添家核心线程执行该任务
    if (addWorker(command, true))
      return;
    //如果添加失败,刷新ctl值
    c = ctl.get();
  }
  //再次检查线程池的运行状态,将任务添加到等待队列中
  if (isRunning(c) && workQueue.offer(command)) {
    
    
    int recheck = ctl.get();//刷新ctl值
    //如果当前线程池的装不是运行状态,那么移除刚才添加的任务
    if (! isRunning(recheck) && remove(command))
      reject(command);//移除成功后,使用拒绝策略处理该任务;
    else if (workerCountOf(recheck) == 0)//当前工作线程数为0
      //线程池正在运行,或者移除任务失败。
      //添加一个非核心线程,并不指定该线程的运行任务。
      //等线程创建完成之后,会从等待队列中获取任务执行。
      addWorker(null, false);
  } 
  //逻辑到这里说明线程池已经不是RUNNING状态,或者等待队列已满,需要创建一个新的非核心线程执行该任务;
  //如果创建失败,那么非核心线程已满,使用拒绝策略处理该任务;
  else if (!addWorker(command, false))
    reject(command);
}

Agregar subprocesos de trabajo y ejecutar tareas

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
  Worker(Runnable firstTask) {
    
    
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;//初始化的任务,可以为null
    this.thread = getThreadFactory().newThread(this);//Worker持有的线程
  }
  /**部分代码省略*/
	public void run() {
    
    
  	runWorker(this);
	}
}

Agregar trabajadores y ejecutar tareas : todo es crear Workery encontrar una coincidencia para ello Runnable.

complemento.png

Añadir subproceso de trabajo

Agregar subprocesos se realiza a través del método en el grupo de subprocesos addWorker. La función de este método es agregar un subproceso. Este método no considera en qué etapa el grupo de subprocesos agrega el subproceso. Esta estrategia de asignación de subprocesos se completa en el paso anterior. Esto el paso es solo Terminar de aumentar el hilo, hacer que se ejecute y finalmente devolver el resultado de si es exitoso o no.

addWorkerEl método tiene dos parámetros: firstTask, core.

firstTaskEl parámetro se usa para especificar la primera tarea ejecutada por el subproceso recién agregado, que puede estar vacío;

coreEl parámetro truesignifica que al agregar un nuevo hilo, juzgará si el número de hilos activos actuales es menor que corePoolSize, falsey significa que necesita juzgar si el número de hilos activos actuales es menor que antes de agregar un nuevo hilo maximumPoolSize.

complemento2.png

private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    retry://breakcontinue的跳出标签
    for (;;) {
    
    
        int c = ctl.get();//获取ctl的值
        int rs = runStateOf(c);//获取当前线程池的状态;
        /**
         * 1、如果当前的线程池状态不是RUNNING
         * 2、当前线程池是RUNNING而且没有添加新任务,而且等待队列不为空。这种情况下是需要创建执行线程的。
         * 所以满足1,但不满足2就创建执行线程失败,返回false。
         */
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
                firstTask == null &&
                ! workQueue.isEmpty()))
            return false;
        /**进入内层循环 */
        for (;;) {
    
    
            int wc = workerCountOf(c);//获取当前执行线程的数量
            /**
             * 1、工作线程数量大于或等于计数器的最大阈值,那么创建执行线程失败,返回false。
             * 2、如果当前创建的核心线程,那么工作线程数大于corePoolSize的话,创建执行线程失败,返回false。
             * 3、如果当前创建的是非核心线程,那么工作线程数大于maximumPoolSize的话,创建执行线程失败,返回false。
             */
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            //用CAS操作让线程数加1,如果成功跳出整个循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)//线程状态前后不一样,重新执行外循环
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
            //如果CAS操作由于工作线程数的增加失败,那么重新进行内循环
        }
    }
    /**就现在,线程数已经增加了。但是真正的线程对象还没有创建出来。*/
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
    
    
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
    
    
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();//加锁
            try {
    
    
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());
                /**
                 * 再次检查线程池的运行状态
                 * 1、如果是RUNNING状态,那么可以创建;
                 * 2、如果是SHUTDOWN状态,但没有执行线程,可以创建(创建后执行等待队列中的任务)
                 */
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
    
    
                    //检测该线程是否已经开启
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);//将改工作线程添加到集合中
                    int s = workers.size();
                    if (s > largestPoolSize)//更新线程池的运行时的最大线程数
                        largestPoolSize = s;
                    workerAdded = true;//标识工作线程添加成功
                }
            } finally {
    
    //释放锁
                mainLock.unlock();
            }
            if (workerAdded) {
    
    //如果工作线程添加成功,那么开启线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
    
    
        //如果工作线程添加失败,那么进行失败处理
        //将已经增加的线程数减少,将添加到集合的工作线程删除
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

realizar tareas

Vimos en la sección de agregar un subproceso de trabajo que después de que la adición sea exitosa, el subproceso se iniciará para ejecutar la tarea.

runwork.png

final void runWorker(Worker w) {
    
    
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    //解锁,允许中断
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    
    
        //如果当前的工作线程已经有执行任务,或者可以从等待队列中获取到执行任务
        //getTask获取任务时候会进行阻塞
        while (task != null || (task = getTask()) != null) {
    
    
            w.lock();//开始执行,上锁
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            //判断线程是否需要中断
            //如果线程池状态是否为STOP\TIDYING\TERMINATED,同时当前线程没有被中断那么将当前线程进行中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                    (Thread.interrupted() &&
                    runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
    
    
                beforeExecute(wt, task);
                Throwable thrown = null;//异常处理
                try {
    
    
                    task.run();
                } catch (RuntimeException x) {
    
    
                    thrown = x; throw x;
                } catch (Error x) {
    
    
                    thrown = x; throw x;
                } catch (Throwable x) {
    
    
                    thrown = x; throw new Error(x);
                } finally {
    
    
                    afterExecute(task, thrown);
                }
            } finally {
    
    
                task = null;//工作线程的当前任务置空
                w.completedTasks++;//当前工作线程执行完成的线程数+1
                w.unlock();//执行完成解锁
            }
        }
        completedAbruptly = false;//完成了所有任务,正常退出
    } finally {
    
    //执行工作线程的退出操作
        processWorkerExit(w, completedAbruptly);
    }
}

El subproceso de trabajo obtiene la tarea.

getTask.jpg

private Runnable getTask() {
    
    
    boolean timedOut = false; // Did the last poll() time out?
    for (;;) {
    
    
        int c = ctl.get();//获取ctl的值
        int rs = runStateOf(c);//获取线程池状态

        // Check if queue empty only if necessary.
        /**
         * 1、rs为STOP\TIDYING\TERMINATED,标识无法继续执行任务
         * 2、等待队列中没有任务可以被执行
         * 工作线程数量减一
         */
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);//获取工作线程数量
        // Are workers subject to culling?
        //如果允许核心线程超时,或者当前工作线程数量大于核心线程数量。标识需要进行超时检测
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        /**
         * 1、如果当前工作线程数是否大于线程池可允许的最大工作线程数(maximumPoolSize可以动态设置)
         * ,或者当前需要进行超时控制并且上次从等待队列中获取执行任务发生了超时。
         * 2、如果当前不是唯一的线程,并且等待队列中没有需要执行的任务。
         * 这两种情况下一起存在就表示,工作线程发生了超时需要回收,所以对线程数进行-1;
         */
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
    
    
            if (compareAndDecrementWorkerCount(c))//线程数量减少成功,否则重新执行本次循环
                return null;
            continue;
        }
        try {
    
    
            //如果设置有超时,那么设定超时时间。否则进行无限的阻塞等待执行任务
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;//获取超时,设置标记
        } catch (InterruptedException retry) {
    
    
            timedOut = false;
        }
    }
}

salida de subproceso de trabajo

La destrucción de subprocesos en el grupo de subprocesos depende del reciclaje automático de la JVM . El trabajo del grupo de subprocesos es mantener un cierto número de referencias de subprocesos de acuerdo con el estado actual del grupo de subprocesos para evitar que estos subprocesos sean reciclados por el JVM Cuando el grupo de subprocesos decide qué subprocesos deben reciclarse, solo debe eliminar su referencia. WorkerDespués de crearse, el sondeo se realizará de forma continua y luego se adquirirá la tarea para ejecutarla. El subproceso central puede esperar indefinidamente para adquirir la tarea, y el subproceso no central debe adquirir la tarea dentro de un tiempo limitado. Cuando Workerno se puede obtener la tarea, es decir, la tarea obtenida está vacía, el bucle finalizará y Workereliminará activamente su propia referencia en el grupo de subprocesos.

procesoTrabajadorSalida.png

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    
    //completedAbruptly为true,标识该工作线程执行出现了异常,将工作线程数减一
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();
    //否则标识该工作线程为正常结束,这种情况下getTask方法中已经对工作线程进行了减一
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();//加锁
    try {
    
    
        completedTaskCount += w.completedTasks;//更新线程池的,线程执行完成数量
        workers.remove(w);//工作线程容器移除该工作线程
    } finally {
    
    
        mainLock.unlock();//解锁
    }
    //尝试结束线程池
    tryTerminate();
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
    
    //如果当前线程池的运行状态是RUNNING\SHUTDOWN
        if (!completedAbruptly) {
    
    //如果该工作线程为正常结束
            /**
             * 判断当前需要的最少的核心线程数(如果允许核心线程超时,那么最小的核心线程数为0,否则为corePoolSize)
             */
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            //如果允许核心线程超时,而且等待队列不为空,那么工作线程的最小值为1,否则为0。
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            //当前工作线程数,是否满足最先的核心线程数
            if (workerCountOf(c) >= min)
                //如果满足那么直接return
                return; // replacement not needed
        }
        //如果是异常结束,或者当前线程数不满足最小的核心线程数,那么添加一个非核心线程
        //核心线程和非核心线程没有什么不同,只是在创建的时候判断逻辑不同
        addWorker(null, false);
    }
}

necesidades especiales

Supervisión de grupos de subprocesos

Supervise a través de los parámetros proporcionados por el grupo de subprocesos. Hay algunos atributos en el grupo de subprocesos que se pueden usar al monitorear el grupo de subprocesos

  • getTaskCount: el número total de tareas ejecutadas y no ejecutadas en el grupo de subprocesos;
  • getCompletedTaskCount: El número de tareas completadas por el grupo de subprocesos, el valor es menor o igual que taskCount;
  • getLargestPoolSize: el número máximo de subprocesos creados por el grupo de subprocesos. A través de estos datos, podemos saber si el grupo de subprocesos está lleno, es decir, se ha alcanzado maximumPoolSize;
  • getPoolSize: El número actual de subprocesos en el grupo de subprocesos;
  • getActiveCount: el número de subprocesos que actualmente ejecutan tareas en el grupo de subprocesos.

Ajuste dinámicamente el tamaño del grupo de subprocesos

JDKPermite al usuario del grupo de subprocesos ThreadPoolExecutorestablecer dinámicamente la política principal del grupo de subprocesos a través de la instancia, setCorePoolSizecomo ejemplo del método;

Después de que el usuario del grupo de subprocesos en tiempo de ejecución llame a esta configuración de método corePoolSize, el grupo de subprocesos sobrescribirá directamente el corePoolSizevalor original y adoptará diferentes estrategias de procesamiento basadas en la comparación entre el valor actual y el valor original.

Para el caso en que el valor actual sea menor que el número actual de subprocesos de trabajo, significa que hay workersubprocesos redundantes. En este momento, se iniciará una solicitud de interrupción al idlesubproceso actual workerpara realizar el reciclaje, y los redundantes también serán reciclado la próxima vez; porque el valor actual es mayor que el valor original y el workeractual para ejecutar las tareas de la cola (PD: el estado es el estado después el hilo libera el bloqueo, porque está bloqueado durante la operación).idelworkeridelworker

establecerCorePoolSize.png

public void setCorePoolSize(int corePoolSize) {
    
    
    if (corePoolSize < 0)
        throw new IllegalArgumentException();
    //计算增量
    int delta = corePoolSize - this.corePoolSize;
    //覆盖原有的corePoolSize
    this.corePoolSize = corePoolSize;
    //如果当前的工作线程数量大于线程池的最大可运行核心线程数量,那么进行中断工作线程处理
    if (workerCountOf(ctl.get()) > corePoolSize)
        interruptIdleWorkers();
    else if (delta > 0) {
    
    //如果增量大于0
        // We don't really know how many new threads are "needed".
        // As a heuristic, prestart enough new workers (up to new
        // core size) to handle the current number of tasks in
        // queue, but stop if queue becomes empty while doing so.
        //等待队列非空,获取等待任务和增量的最小值
        int k = Math.min(delta, workQueue.size());
        //循环创建核心工作线程执行等待队列中的任务
        while (k-- > 0 && addWorker(null, true)) {
    
    
            if (workQueue.isEmpty())
                break;
        }
    }
}
private void interruptIdleWorkers() {
    
    
    interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
    
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();//加锁
    try {
    
    
        //遍历工作线程的集合
        for (Worker w : workers) {
    
    
            Thread t = w.thread;
            //如果当前线程没有被中断,而且能获取到锁,那么尝试进行中断,最后释放锁
            if (!t.isInterrupted() && w.tryLock()) {
    
    
                try {
    
    
                    t.interrupt();
                } catch (SecurityException ignore) {
    
    
                } finally {
    
    
                    w.unlock();
                }
            }
            //是否仅仅中断一个工作线程
            if (onlyOne)
                break;
        }
    } finally {
    
    //释放锁
        mainLock.unlock();
    }
}

Cierra con gracia el grupo de subprocesos

También se puede ver en el diagrama "Ciclo de instrucción del grupo de subprocesos" que cuando ejecutamos ThreadPoolExecutor#shutdownel método el estado del grupo de subprocesos cambiará de EJECUTAR a APAGADO . ThreadPoolExecutor#shutdownNowDespués de la llamada, el estado del grupo de subprocesos cambiará de RUNNING a STOP .

cerrar

Deja de aceptar nuevas tareas y las tareas originales continúan ejecutándose

  1. Deja de recibir nuevas tareas de envío;
  2. Las tareas que se han enviado (incluidas las que se están ejecutando y las que esperan en la cola) seguirán ejecutándose;
  3. Espere hasta que se complete el paso 2 antes de detenerse;
public void shutdown() {
    
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        checkShutdownAccess();// 检查权限
        advanceRunState(SHUTDOWN);// 设置线程池状态
        interruptIdleWorkers();// 中断空闲线程
        // 钩子函数,主要用于清理一些资源
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
    
    
        mainLock.unlock();
    }
    tryTerminate();
}

shutdownEl método primero bloquea y luego verifica primero el estado de instalación del sistema. Luego, el estado del grupo de subprocesos cambiará a SHUTDOWN , después de lo cual el grupo de subprocesos ya no aceptará nuevas tareas enviadas. En este momento, si continúa enviando tareas al grupo de subprocesos, se usará la política de rechazo del grupo de subprocesos para responder, que se usará de manera predeterminada y se generará una excepciónThreadPoolExecutor.AbortPolicy .RejectedExecutionException

interruptIdleWorkersEl método se describe en el código fuente en la sección de ajuste dinámico del tamaño del grupo de subprocesos Solo interrumpirá los subprocesos inactivos y no interrumpirá los subprocesos que están ejecutando tareas. Los subprocesos inactivos se bloquearán en la cola de bloqueo del grupo de subprocesos.

apagar ahora

Deja de aceptar nuevas tareas y las tareas originales dejan de ejecutarse

  1. Igual shutdown() que , primero deja de recibir nuevas submittareas;
  2. Ignora las tareas que esperan en la cola;
  3. Intentar interrumpir la ejecución de la tarea interrupt;
  4. Devuelve una lista de tareas no ejecutadas;
public List<Runnable> shutdownNow() {
    
    
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        checkShutdownAccess();// 检查状态
        advanceRunState(STOP);// 将线程池状态变为 STOP
        interruptWorkers();// 中断所有线程,包括工作线程以及空闲线程
        tasks = drainQueue();// 丢弃工作队列中存量任务
    } finally {
    
    
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}
private void interruptWorkers() {
    
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        for (Worker w : workers)
            //如果工作线程已经开始,那么调用interrupt进行中断
            w.interruptIfStarted();
    } finally {
    
    
        mainLock.unlock();
    }
}
private List<Runnable> drainQueue() {
    
    
    BlockingQueue<Runnable> q = workQueue;
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    //从此队列中删除所有可用的元素,并将它们添加到给定的集合中。
    q.drainTo(taskList);
    //如果队列是DelayQueue或其他类型的队列,而poll或drainTo可能无法删除某些元素,则会将它们逐个删除。
    if (!q.isEmpty()) {
    
    
        for (Runnable r : q.toArray(new Runnable[0])) {
    
    
            if (q.remove(r))
                taskList.add(r);
        }
    }
    return taskList;
}

shutdownNowEl método para intentar terminar el subproceso se realiza llamando Thread.interrupt()al método , que tiene un efecto limitado. Si no hay aplicaciones como sleep, wait, , time lock en el subproceso, el método no puede interrumpir el subproceso actual. Por lo tanto, no significa que el grupo de subprocesos podrá salir inmediatamente, y es posible que tenga que esperar a que se completen todas las tareas en ejecución antes de salir. Pero la mayoría de las veces es posible dejar de fumar de inmediato.Conditioninterrupt()shutdownNow()

Mecanismo de interrupción de subprocesos: thread#interruptsimplemente establecer un indicador de interrupción no interrumpirá inmediatamente los subprocesos normales. Si desea que la interrupción surta efecto inmediatamente, debe llamar al subproceso para Thread.interrupted()juzgar el estado de interrupción del subproceso. Para un subproceso bloqueado, cuando se llama a la interrupción, el subproceso saldrá inmediatamente del estado bloqueado y generará InterruptedExceptionuna excepción . Entonces, para los hilos bloqueados, InterruptedExceptionlas excepciones .

esperarTerminación

El grupo de subprocesos shutdowny shutdownNowel método no esperarán activamente hasta el final de la tarea de ejecución. Si necesita esperar hasta el final de la ejecución de la tarea del grupo de subprocesos, debe llamar a awaitTerminationActively of the task call.

  • Espere hasta que se ejecuten todas las tareas enviadas (incluida la ejecución y la espera en la cola);
  • Espere hasta que expire el tiempo de espera;
  • el hilo se interrumpe, tira InterruptedException;

Si finaliza la ejecución de la tarea del grupo de subprocesos, awaitTerminationel método devolverá true; de lo contrario, volverá cuando el tiempo de espera exceda el tiempo especificado false.

// 关闭线程池的钩子函数
private static void shutdown(ExecutorService executorService) {
    
    
    // 第一步:使新任务无法提交
    executorService.shutdown();
    try {
    
    
        // 第二步:等待未完成任务结束
        if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
    
    
             // 第三步:取消当前执行的任务
            executorService.shutdownNow();
            // 第四步:等待任务取消的响应
            if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
    
    
                System.err.println("Thread pool did not terminate");
            }
        }
    } catch(InterruptedException ie) {
    
    
        // 第五步:出现异常后,重新取消当前执行的任务
        executorService.shutdownNow();
        Thread.currentThread().interrupt(); // 设置本线程中断状态
    }
}

otro

¡Siento que el contenido es tanto que no puedo terminarlo~!

Cuando hablamos del grupo de subprocesos, tenemos que hablar de operaciones concurrentes de subprocesos múltiples, 同步, 异步, CSA, AQS, y otros puntos de conocimiento necesarios para el control de concurrencia.公平锁和非公平锁可重入锁和非可重入锁

Rara vez se utiliza en el trabajo diario ¿Tiene una estructura de conocimiento sistemática? Conduce a olvidar después de aprender mucho, y luego a aprender y olvidar de nuevo.

Espero tener la oportunidad de aprender y compartir paso a paso en el futuro.

El artículo se cuenta aquí, si tiene otras necesidades para comunicarse, ¡puede dejar un mensaje ~!

Supongo que te gusta

Origin blog.csdn.net/stven_king/article/details/113870331
Recomendado
Clasificación