8. Herramienta para compartir el grupo de subprocesos de modelo

8.1 Grupo de subprocesos
2. ThreadPoolExecutor

1) Estado del grupo de subprocesos
ThreadPoolExecutor usa los 3 bits altos de int para indicar el estado del grupo de subprocesos, y los 29 bits bajos indican el número de subprocesos
 

A partir de una comparación numérica, TERMINATED> TIDYING> STOP> SHUTDOWN> RUNNING
Esta información se almacena en una variable atómica ctl, el propósito es combinar el estado del grupo de subprocesos y el número de subprocesos en uno, de modo que se pueda usar una operación atómica cas
para asignación

// c 为旧值, ctlOf 返回结果为新值
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))));
// rs 为高 3 位代表线程池状态, wc 为低 29 位代表线程个数,ctl 是合并它们
private static int ctlOf(int rs, int wc) { return rs | wc; }

2) Método de construcción

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
  • corePoolSize Número de subprocesos centrales (número máximo de subprocesos reservados)
  • maximumPoolSize número máximo de subprocesos
  • keepAliveTime-para el hilo de emergencia
  • unidad de tiempo unidad-para el hilo de emergencia
  • cola de bloqueo workQueue
  • threadFactory Thread Factory: puede dar un buen nombre al hilo cuando se crea
  • estrategia de rechazo del controlador

Manera de trabajar:

  • No hay subprocesos en el grupo de subprocesos al principio. Cuando se envía una tarea al grupo de subprocesos, el grupo de subprocesos creará un nuevo subproceso para ejecutar la tarea.
  • Cuando el número de subprocesos alcanza corePoolSize y no hay subprocesos inactivos, agregue tareas en este momento y las tareas recién agregadas se agregarán a la cola workQueue hasta que haya subprocesos inactivos.
  • Si se selecciona una cola limitada para la cola, cuando la tarea excede el tamaño de la cola, se creará una cantidad máxima de subprocesos para ayudar.
  • Si el hilo alcanza el maximumPoolSize y todavía hay nuevas tareas, se ejecutará la estrategia de rechazo. La estrategia de rechazo jdk proporciona 4 implementaciones, y otros marcos bien conocidos también brindan implementaciones
  1. AbortPolicy permite a la persona que llama lanzar la excepción RejectedExecutionException, esta es la política predeterminada
  2. CallerRunsPolicy permite a la persona que llama ejecutar tareas
  3. DiscardPolicy abandona esta tarea
  4. DiscardOldestPolicy abandona la tarea más antigua de la cola, esta tarea la reemplaza
  5. La implementación de Dubbo registrará el registro antes de lanzar RejectedExecutionException y volcará la información de la pila de subprocesos.
  6. Localizando el problema
  7. La implementación de Netty es crear un nuevo hilo para realizar tareas.
  8. La implementación de ActiveMQ, con tiempo de espera de espera (60 s) para intentar ponerlo en la cola, similar a nuestra estrategia de rechazo personalizada anterior
  9. La implementación de PinPoint, que utiliza una cadena de estrategias de rechazo, probará cada estrategia de rechazo en la cadena de estrategias una por una.

Una vez que ha pasado el pico, si el hilo de emergencia que excede corePoolSize no tiene ninguna tarea que hacer durante un período de tiempo, debe finalizar para ahorrar recursos. Este tiempo está controlado por keepAliveTime y unit

De acuerdo con este método de construcción, muchos métodos de fábrica se proporcionan en la clase JDK Executors para crear grupos de subprocesos para varios propósitos
3) newFixedThreadPool

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

Características

  • El número de subprocesos centrales == el número máximo de subprocesos (no se crea ningún subproceso de emergencia), por lo que no hay necesidad de tiempo de espera
  • La cola de bloqueo no tiene límites y puede colocar cualquier cantidad de tareas.

La evaluación es adecuada para tareas con un volumen de tareas conocido y tareas que requieren mucho tiempo.

4) newCachedThreadPool

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

Características La
cantidad de subprocesos centrales es 0, la cantidad máxima de subprocesos es Integer.MAX_VALUE, el tiempo de supervivencia inactivo de los subprocesos de emergencia es 60 s, lo que significa que
todos son subprocesos de emergencia (se pueden reciclar después de 60 s)

Los hilos de emergencia se pueden crear infinitamente

La cola adopta SynchronousQueue. La característica es que no tiene capacidad y no se puede colocar sin hilos para buscar (pagar en una mano y entregar en una mano)
 

SynchronousQueue<Integer> integers = new SynchronousQueue<>();
new Thread(() -> {
try {
log.debug("putting {} ", 1);
integers.put(1);
log.debug("{} putted...", 1);
log.debug("putting...{} ", 2);
integers.put(2);
log.debug("{} putted...", 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1").start();
sleep(1);
new Thread(() -> {
try {
log.debug("taking {}", 1);
integers.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t2").start();
sleep(1);
new Thread(() -> {
try {
log.debug("taking {}", 2);
integers.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t3").start();

Producción

11:48:15.500 c.TestSynchronousQueue [t1] - putting 1
11:48:16.500 c.TestSynchronousQueue [t2] - taking 1
11:48:16.500 c.TestSynchronousQueue [t1] - 1 putted...
11:48:16.500 c.TestSynchronousQueue [t1] - putting...2
11:48:17.502 c.TestSynchronousQueue [t3] - taking 2
11:48:17.503 c.TestSynchronousQueue [t1] - 2 putted...

El rendimiento de evaluar todo el grupo de subprocesos es que el número de subprocesos aumentará de acuerdo con la cantidad de tareas y no hay límite superior. Cuando se ejecuta la tarea, el subproceso se liberará después de 1 minuto de tiempo de inactividad
. Adecuado para situaciones en las que el número de tareas es intenso, pero el tiempo de ejecución de cada tarea es corto.

5) newSingleThreadExecutor

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

Escenario de uso:
espero que se ejecuten varias tareas en una cola. El número de subprocesos se fija en 1, y cuando el número de tareas es superior a 1, se pondrán en cola en una cola ilimitada. Una vez ejecutada la tarea,
no se liberará el único hilo .
la diferencia:

  • Cree usted mismo una tarea de ejecución en serie de un solo subproceso. Si la ejecución de la tarea falla y finaliza, no hay solución y el grupo de subprocesos creará un nuevo subproceso para garantizar el trabajo normal del grupo.
  • Executors.newSingleThreadExecutor () El número de subprocesos es siempre 1 y no se puede modificar
  1. FinalizableDelegatedExecutorService usa el modo decorador, que solo expone la interfaz ExecutorService al exterior, por lo que los métodos únicos en ThreadPoolExecutor no se pueden llamar
  2. Executors.newFixedThreadPool (1) es 1 inicialmente, se puede modificar más tarde
  3. Lo que está expuesto al exterior es el objeto ThreadPoolExecutor, que se puede modificar llamando a setCorePoolSize y a otros métodos después de la conversión forzada.
     

 6) Enviar tarea

// 执行任务
void execute(Runnable command);
// 提交任务 task,用返回值 Future 获得任务执行结果
<T> Future<T> submit(Callable<T> task);
// 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
// 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;

7) Cierre la agrupación de hebras
de apagado

public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态
advanceRunState(SHUTDOWN);
// 仅会打断空闲线程
interruptIdleWorkers();
onShutdown(); // 扩展点 ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
tryTerminate();
}

shutdownNow
 

/*
线程池状态变为 STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用 interrupt 的方式中断正在执行的任务
*/
List<Runnable> shutdownNow();
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态
advanceRunState(STOP);
// 打断所有线程
interruptWorkers();
// 获取队列中剩余任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 尝试终结
tryTerminate();
return tasks;
}

Otros metodos

// 不在 RUNNING 状态的线程池,此方法就返回 true
boolean isShutdown();
// 线程池状态是否是 TERMINATED
boolean isTerminated();
// 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池 TERMINATED 后做些事
情,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

8) Hilo de programación de tareas

Antes de que se agregue la función "grupo de subprocesos de programación de tareas", se puede usar java.util.Timer para implementar la función de temporización. La ventaja de Timer es que es simple y fácil de usar, pero porque todas las tareas están programadas por el mismo subproceso , todas las tareas son cadenas. Si se ejecuta, solo se puede ejecutar una tarea al mismo tiempo, y el retraso o excepción de la tarea anterior afectará a las tareas posteriores.

public static void main(String[] args) {
Timer timer = new Timer();
TimerTask task1 = new TimerTask() {
@Override
public void run() {
log.debug("task 1");
sleep(2);
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
log.debug("task 2");
}
};
// 使用 timer 添加两个任务,希望它们都在 1s 后执行
// 但由于 timer 内只有一个线程来顺序执行队列中的任务,因此『任务1』的延时,影响了『任务2』的执行
timer.schedule(task1, 1000);
timer.schedule(task2, 1000);
}

Producción

20: 46: 09.444 c.TestTimer [principal] - inicio ...
20: 46: 10.447 c.TestTimer [Timer-0] - tarea 1
20: 46: 12.448 c.TestTimer [Timer-0] - tarea 2

Utilice ScheduledExecutorService para reescribir:

ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
// 添加两个任务,希望它们都在 1s 后执行
executor.schedule(() -> {
System.out.println("任务1,执行时间:" + new Date());
try { Thread.sleep(2000); } catch (InterruptedException e) { }
}, 1000, TimeUnit.MILLISECONDS);
executor.schedule(() -> {
System.out.println("任务2,执行时间:" + new Date());
}, 1000, TimeUnit.MILLISECONDS);


Tarea de salida 1, tiempo de ejecución: jueves 03 de enero 12:45:17 CST 2019
Tarea 2, tiempo de ejecución: jueves 03 de enero 12:45:17 CST 2019

Ejemplo de scheduleAtFixedRate
 

ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
log.debug("start...");
pool.scheduleAtFixedRate(() -> {
log.debug("running...");
}, 1, 1, TimeUnit.SECONDS);

Salida
21: 45: 43.167 c.TestTimer [main] -start ...
21: 45: 44.215 c.TestTimer [pool-1-thread-1]
-running ... 21: 45: 45.215 c.TestTimer [pool- 1-hilo-1]
-ejecutando ... 21: 45: 46.215 c.TestTimer [pool-1-thread-1]
-running ... 21: 45: 47.215 c.TestTimer [pool-1-thread-1] -running ..
ejemplo de scheduleAtFixedRate (el tiempo de ejecución de la tarea excede el tiempo del intervalo):
 

ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
log.debug("start...");
pool.scheduleAtFixedRate(() -> {
log.debug("running...");
sleep(2);
}, 1, 1, TimeUnit.SECONDS);

Análisis de salida: al principio, la demora es de 1 s, y luego, debido a que el tiempo de ejecución de la tarea> el tiempo del intervalo, el intervalo se "estira" a 2 s.

21: 44: 30.311 c.TestTimer [principal] - inicio ...
21: 44: 31.360 c.TestTimer [pool-1-thread-1] - en ejecución ...
21: 44: 33.361 c.TestTimer [pool-1 -thread-1] - en ejecución ...
21: 44: 35.362 c.TestTimer [pool-1-thread-1] - en ejecución ...
21: 44: 37.362 c.TestTimer [pool-1-thread-1] - corriendo...

Ejemplo de scheduleWithFixedDelay:

10) Grupo de subprocesos de Tomcat
¿Dónde utiliza Tomcat el grupo de subprocesos?

  • LimitLatch se usa para limitar la corriente y puede controlar el número máximo de conexiones, similar a Semaphore en JUC, lo hablaré más adelante
  • El aceptador solo es responsable de [recibir nuevas conexiones de socket]
  • Poller solo es responsable de monitorear si el canal de socket tiene [eventos de E / S legibles]
  • Una vez que sea legible, encapsule un objeto de tarea (socketProcessor) y envíelo al grupo de subprocesos del Ejecutor para su procesamiento
  • Los subprocesos de trabajo en el grupo de subprocesos del ejecutor son los responsables en última instancia de [procesar las solicitudes]

El grupo de subprocesos de Tomcat extiende ThreadPoolExecutor, y el comportamiento es ligeramente diferente.
Si el número total de subprocesos alcanza el maximumPoolSize,
RejectedExecutionException no se lanzará inmediatamente.
En su lugar, intentará poner la tarea en la cola nuevamente. Si aún falla, el Se lanzará la
excepción RejectedExecutionException. Código fuente tomcat-7.0.42

Configuración del hilo del ejecutor

Supongo que te gusta

Origin blog.csdn.net/nmjhehe/article/details/109629462
Recomendado
Clasificación