2. Introducción
La tecnología de lectura múltiple resuelve principalmente el problema de la ejecución de múltiples hilos en una unidad de procesador. Puede reducir significativamente el tiempo de inactividad de la unidad del procesador y aumentar la capacidad de rendimiento de la unidad del procesador. Sin embargo, la sobrecarga de la creación de hilos frecuentes es muy grande. Entonces, cómo reducir esta parte de la sobrecarga, debe considerar usar un grupo de hilos. Un grupo de subprocesos es un contenedor de subprocesos, que solo ejecuta un número nominal de hilos a la vez. El grupo de subprocesos se usa para administrar este número de subprocesos nominal.
3. Diagrama de estructura de clase que involucra a la piscina de subprocesos
Entre ellos, el principal para usar es la clase Threadpoolexecutor.
4. Cómo crear una piscina de hilos
Generalmente tenemos los siguientes métodos para crear grupos de subprocesos:
1. Use la clase de fábrica de ejecutores
Los ejecutores proporcionan principalmente los siguientes métodos para crear grupos de subprocesos:
Echemos un vistazo al ejemplo de uso a continuación:
1) NewFixedThreadPool (grupo de subprocesos fijo)
clase pública FIRETATHREADPOOL {public static void main (String [] args) {ExecutorService Pool = Ejecutors.NewFixedThreadPool (5); // Cree un grupo de hilos con un tamaño fijo de 5 para (int i = 0; i <10; i ++) {piscal.submit (new mythread ()); } prow.shutdown (); }} public class myThread extiende el hilo {@Override public void run () {System.out.println (Thread.CurrentThread (). GetName () + "Ejecución ..."); }}Los resultados de la prueba son los siguientes:
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-2 se está ejecutando. . .
Pool-1-Thread-3 se está ejecutando. . .
Pool-1-Thread-2 se está ejecutando. . .
Pool-1-Thread-3 se está ejecutando. . .
Pool-1-Thread-2 se está ejecutando. . .
Pool-1-Thread-2 se está ejecutando. . .
Pool-1-Thread-3 se está ejecutando. . .
Se está ejecutando Pool-1-Thread-5. . .
Pool-1-Thread-4 se está ejecutando. . .
Grupo de subprocesos de tamaño fijo: cree un hilo cada vez que se envíe una tarea, hasta que el hilo alcance el tamaño máximo del grupo de subprocesos. El tamaño del grupo de hilo permanecerá sin cambios una vez que alcance su valor máximo. Si un hilo termina debido a una excepción de ejecución, el grupo de subprocesos agregará un nuevo hilo.
2) NewsingLethreadExecutor (grupo de hilos de un solo hilo)
public class SinglethreadPool {public static void main (String [] args) {ExecutorService Pool = Ejecutors.NeWSingLethreadExecutor (); // Cree un solo grupo de hilo para (int i = 0; i <100; i ++) {piscal.subMit (nuevo mythread ()); } prow.shutdown (); }}Los resultados de la prueba son los siguientes:
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Grupo de subprocesos de un solo hilo: este grupo de hilos solo tiene un hilo funcionando, lo que significa que una ejecución en serie de todas las tareas de todas las tareas. Si este hilo único termina debido a la excepción, entonces habrá un nuevo hilo para reemplazarlo. Este grupo de subprocesos asegura que la orden de ejecución de todas las tareas se ejecute en el orden de la presentación de la tarea.
3) NewscheduledThreadPool
clase pública ProchuledThreadPool {public static void main (string [] args) {ProgramedExecutorService Pool = Ejecutors.NewScheduledThreadPool (6); para (int i = 0; i <10000; i ++) {prow.submit (new MyThread ()); } Pool.schedule (nuevo MyThread (), 1000, TimeUnit.MilliseConds); Pool.schedule (nuevo MyThread (), 1000, TimeUnit.MilliseConds); Pool.shutdown (); }}Los resultados de la prueba son los siguientes:
Pool-1-Thread-1 se está ejecutando. . .
Pool-1-Thread-6 se está ejecutando. . .
Se está ejecutando Pool-1-Thread-5. . .
Pool-1-Thread-4 se está ejecutando. . .
Pool-1-Thread-2 se está ejecutando. . .
Pool-1-Thread-3 se está ejecutando. . .
Pool-1-Thread-4 se está ejecutando. . .
Se está ejecutando Pool-1-Thread-5. . .
Pool-1-Thread-6 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
……………………………………………………………………………………………………………………………………………………………………… ………………………………………………………………………………………………………………………………………………………………………
Pool-1-Thread-4 se está ejecutando. . .
Pool-1-Thread-1 se está ejecutando. . .
Los últimos dos hilos del resultado de la prueba solo comienzan a ejecutarse después de retrasar 1s. Este grupo de subprocesos admite el requisito de tiempo y ejecución periódica de tareas
4) NewcachedThreadPool (grupo de hilos almacenables)
clase pública CachedThreadPool {public static void main (String [] args) {ExecutorService Pool = Ejecutors.newCachedThreadPool (); para (int i = 0; i <100; i ++) {prow.submit (new MyThread ()); } prow.shutdown (); }}Los resultados de la prueba son los siguientes:
Se está ejecutando Pool-1-Thread-5. . .
Pool-1-Thread-7 se está ejecutando. . .
Se está ejecutando Pool-1-Thread-5. . .
Pool-1-Thread-16 está en ejecución. . .
Pool-1-Thread-17 se está ejecutando. . .
Pool-1-Thread-16 está en ejecución. . .
Se está ejecutando Pool-1-Thread-5. . .
Pool-1-Thread-7 se está ejecutando. . .
Pool-1-Thread-16 está en ejecución. . .
Pool-1-Thread-18 se está ejecutando. . .
Pool-1-Thread-10 se está ejecutando. . .
Grupo de subprocesos almacenables: si el tamaño del grupo de subprocesos excede el hilo requerido para procesar la tarea, se reciclarán algunos subprocesos inactivos (sin ejecución de la tarea en 60 segundos). Cuando aumenta el número de tareas, este grupo de subprocesos puede agregar inteligentemente nuevos hilos para manejar la tarea. Este grupo de subprocesos no limita el tamaño del grupo de subprocesos, que depende completamente del tamaño máximo de hilo que el sistema operativo (o JVM) puede crear.
El funcionario sugiere que los programadores usan los ejecutores de métodos de fábrica más convenientes. Estos grupos de subprocesos están predefinidos por configuración predeterminada para la mayoría de los escenarios de uso.
2. Herve la clase ThreadPoolExeCutor y copie el método de constructor de la clase principal.
Antes de introducir este método, analicemos los códigos subyacentes anteriores para crear grupos de subprocesos.
Ejecutores de clase pública {public static ejecutorservice newfixedThreadPool (int nthreads) {return New ThreadPoolExecutor (nthreads, nthreads, 0l, timeUnit.milliseConds, new LinkedBlockingqueue <Runnable> ()); } public static EjecutorService NewsingLethreadExecutor () {return new FinalInsegeLegedExecutorService (new ThreadPoolExecutor (1, 1, 0L, TimeUnit.MilliseConds, New LinkedBlockingqueue <Runnable> ())); }}Desde el código subyacente de la clase de fábrica de ejecutores, podemos ver que los métodos proporcionados por la clase de fábrica para crear grupos de subprocesos se implementan realmente mediante la construcción de ThreadPoolExecutor. El código de método de constructor de threadpoolexeCutor es el siguiente:
Public ThreadPoolExeCutor (int corePoolSize, int MaximumummoolSize, Long KeepAliveTime, TimeUnit Unit, Bloquingqueue <Runnable> WorkQueue, ThreadFactory ThreadFactory, RecheedEdEdEdExeCutionHandler Handler) {if (corePoolSize <0 || maximumumumeolSize <= 0 || maximoMupoolSize <corePoolSize || keepAlIteTime <0 || maximumumumsize <= 0 || maximumUptOn IlegalargumentException (); if (workqueue == null || threadFactory == null || handler == null) tirar nueva nullpointerException (); this.corepoolSize = corePoolSize; this.maxiMumumpoolSize = maximumumeMoLSize; this.WorkQueue = WorkQueue; this.keepaliveTime = unit.tonanos (keepAlivEtime); this.threadFactory = ThreadFactory; this.handler = Handler; }Luego, hablemos sobre el método del constructor ThreadpoolExeCutor. En este método de construcción, existen principalmente los siguientes parámetros:
CorePoolSize: la cantidad de hilos guardados en la piscina, incluidos los hilos gratuitos.
MAXIMUMPOOLSIZE: el número máximo de hilos permitidos en la piscina.
KeepAliveTime: cuando el número de hilos es mayor que CorePoolSize, este es el momento más largo para que el hilo inactivo espere una nueva tarea.
Unidad- Unidad de tiempo de parámetro KeepAliveTime.
WorkQueue: la cola utilizada para mantener tareas antes de la ejecución. Esta cola solo mantiene tareas ejecutables enviadas por el método de ejecución.
ThreadFactory: la fábrica utilizada por los ejecutores para crear nuevos hilos.
Manejador: el controlador utilizado cuando la ejecución se bloquea debido al alcance del hilo y la capacidad de la cola.
A continuación, hablemos sobre la relación entre estos parámetros. Cuando se acaba de crear el grupo de subprocesos, no hay hilos en el grupo de subprocesos (tenga en cuenta que no es que se cree un cierto número de hilos tan pronto como se crea el grupo de subprocesos). Cuando se llama al método Execute () para agregar una tarea, el grupo de subprocesos hará el siguiente juicio:
1) Si el número de hilos que se ejecutan actualmente es menor que CorePoolSize, cree un nuevo hilo inmediatamente para realizar esta tarea.
2) Si el número de hilos que se ejecutan actualmente es mayor o igual a CorePoolSize, entonces esta tarea se colocará en la cola.
3) Si la cola de grupo de subprocesos está llena, pero el número de hilos en ejecución es menor que MaximumumpolSize, aún se creará un nuevo hilo para realizar esta tarea.
4) Si la cola está llena y el número de subprocesos actualmente en ejecución es mayor o igual a MaximumumpoolSize, el grupo de subprocesos manejará la tarea actual en función de la política de rechazo.
5) Cuando se ejecuta una tarea, el hilo tomará la siguiente tarea de la cola para ejecutar. Si no hay ninguna tarea que se ejecute en la cola, entonces el hilo estará inactivo. Si el tiempo de supervivencia de KeepAlivetime excede, el hilo se reciclará con el grupo de subprocesos (nota: los hilos de reciclaje son condicionales. Si el número de hilos que se ejecuta actualmente es mayor que CorePoolSize, el hilo se destruirá. ¿Por qué no es que el hilo se recicle tan pronto como está inactivo, sino que debe esperar hasta que exceda KeepAlivetime antes de que el hilo se recicle? La razón es muy simple: porque la creación y destrucción de los hilos consumen mucho, y no se puede crear y destruir con frecuencia. Después de superar a KeepAlivetime, se encuentra que este hilo de hecho no se usa y será destruido. En este caso, la unidad representa la unidad de tiempo de KeepAlivetime, y la definición de unidad es la siguiente:
public enum enum timeUnit {nanoseConds {// keepAliveTime en nanoseConds}, microseConds {// keepaliveTime en microsegundos}, miliseConds {// keepAlivetime en MilliseConds}, Sounds {// Keepalivetime en segundos}, minutos { / /KeepAtime en minutos}, Sounds {// KeepAlivetime en horas}, días {// KeepAlivEtime en días}; Analicemos el código fuente a continuación. Para las situaciones anteriores, los códigos de origen involucrados principalmente son los siguientes:
privado boolean addifundunderCorePoolSize (runnable firstTask) {hilo t = null; Reentrantlock final Mainlock = this.MainLock; mainlock.lock (); Pruebe {if (PoolSize <CorePoolSize && RunState == Running) t = addThread (firstTask); } finalmente {mainlock.unlock (); } if (t == null) return false; t.Start (); devolver verdadero; } De hecho, este código es muy simple. Describe principalmente que si el grupo de subprocesos actual es más pequeño que CorePoolSize, se crea un nuevo hilo para manejar la tarea.
privado boolean addifunderMaxiMumpeolSize (Runnable FirstTask) {Thread t = null; Reentrantlock final Mainlock = this.MainLock; mainlock.lock (); Pruebe {if (PoolSize <MaximumumeLSize && runState == Running) t = addThread (firstTask); } finalmente {mainlock.unlock (); } if (t == null) return false; t.Start (); devolver verdadero; }El código anterior describe que si el número de grupos de subprocesos actuales es menor que el maximoMoolSize, se creará un hilo para ejecutar la tarea.
5. La cola de la piscina de hilos
Hay 3 tipos de colas de piscina de hilos:
Conjunto directo: la opción predeterminada para las colas de trabajo es Synchronousqueue, que envía tareas directamente a los subprocesos sin mantenerlos. Aquí, si no hay ningún hilo disponible para ejecutar la tarea de inmediato, intentar colocar la tarea fallará, construyendo así un nuevo hilo. Esta política evita bloqueos al manejar conjuntos de solicitudes que pueden tener dependencias internas. Las presentaciones directas generalmente requieren Maximumumpolizes ilimitados para evitar rechazar las tareas recién presentadas. Esta estrategia permite que los hilos ilimitados tengan la posibilidad de crecimiento cuando los comandos llegan continuamente con un promedio que la cola puede manejar.
La cola ilimitada: el uso de una cola ilimitada (por ejemplo, Linked Bloquingqueue sin capacidad predefinida) hará que las nuevas tareas esperen en la cola cuando todos los hilos de CorePoolSize estén ocupados. De esta manera, el hilo creado no excederá CorePoolSize. (El valor de MaximumumpoolSize no es válido). Cuando cada tarea es completamente independiente de otras tareas, es decir, la ejecución de la tarea no se afecta entre sí, es adecuada para colas ilimitadas; Por ejemplo, en un servidor de página web. Esta cola se puede usar para manejar las solicitudes de ráfaga transitoria, y esta estrategia permite que los hilos ilimitados tengan la posibilidad de crecimiento cuando los comandos llegan continuamente excediendo el promedio que la cola puede manejar.
Cola limitada: cuando se usa Maximumumpoolizes limitados, colas limitadas (como ArrayBlockingqueue) ayudan a prevenir el agotamiento de los recursos, pero pueden ser difíciles de ajustar y controlar. Es posible que el tamaño de la cola y el tamaño máximo de la piscina se necesiten entre sí: el uso de colas grandes y las piscinas pequeñas puede minimizar el uso de la CPU, los recursos del sistema operativo y la sobrecarga de conmutación de contexto, pero puede dar lugar a una reducción manual en el rendimiento. Si las tareas se bloquean con frecuencia (por ejemplo, si son límites de E/S), el sistema puede programar el tiempo para más hilos de los que permite. El uso de colas pequeñas generalmente requiere un tamaño de piscina más grande y tiene un uso de CPU más alto, pero puede encontrar una sobrecarga de programación inaceptable, lo que también puede reducir el rendimiento.
Hablemos de la cola de la piscina de hilos a continuación, el diagrama de la estructura de la clase es el siguiente:
1) Synchronousqueue
La cola corresponde a la presentación directa mencionada anteriormente. En primer lugar, Synchronousqueue está ilimitado, lo que significa que su capacidad para almacenar números es ilimitada. Sin embargo, debido a las características de la cola en sí, después de agregar elementos, debe esperar a que otros hilos los lleven antes de poder continuar agregando.
2) Linked Bloquingqueue
La cola corresponde a la cola ilimitada anterior.
3) Arrayblokingqueue
La cola corresponde a la cola limitada arriba. ArrayBlockingqueue tiene los siguientes 3 constructores:
Public ArrayBlockingqueue (int capacidad) {this (capacidad, falso); } Public ArrayBlockingqueue (int capacidad, boolean fair) {if (capacidad <= 0) tirar nueva ilegalArgumentException (); this.items = (e []) nuevo objeto [capacidad]; bloqueo = nuevo reentrantlock (justo); noTempty = Lock.NewCondition (); notfull = lock.newcondition (); } Public ArrayBlockingqueue (int capacidad, boolean fair, colección <? extiende e> c) {this (capacidad, justa); if (capacidad <c.size ()) tirar nueva ilegalargumentException (); para (iterator <? extiende e> it = c.iterator (); it.hasnext ();) add (it.next ()); }Centrémonos en esta feria. Fair representa la estrategia de competencia de los hilos de acceso a la cola. Cuando es cierto, las colas de inserción de la tarea cumplen con las reglas FIFO. Si False, puede "cortar la cola". Por ejemplo, si ahora hay muchas tareas en cola, un hilo ha completado la tarea y se produce una nueva tarea. Si es falso, esta tarea no necesita ser cola en la cola. Puede cortar directamente la cola y luego ejecutarla. Como se muestra en la figura a continuación:
6. Estrategia de ejecución de rechazo de grupo de hilos
Cuando el número de subprocesos alcanza el valor máximo, las tareas aún llegan en este momento, y en este momento, tengo que negarme a aceptar las tareas.
ThreadPoolExecutor permite la personalización de las políticas de ejecución al agregar una tarea falla. Puede llamar al método setRejectedExeCutionHandler () del grupo de subprocesos y reemplazar la política existente con el objeto personalizado de rechazo, EXECUCIONSHANDLER. La estrategia de procesamiento predeterminada proporcionada por ThreadPoolExecutor es descartar directamente y arrojar información de excepción al mismo tiempo. ThreadPoolExecutor proporciona 4 políticas existentes, a saber:
Threadpoolexecutor.abortpolicy: indica que la tarea se rechaza y se lanza una excepción. El código fuente es el siguiente:
Public static class abortpolicy implementa rechazarexecutionHandler { /*** crea una <tt> abortpolicy </tt>. * / public abortpolicy () {} / *** Siempre lanza RecheedExecutionException. * @param r La tarea ejecutable solicitada para ser ejecutada * @param e el ejecutor que intenta ejecutar esta tarea * @throws rechazadoxecutionException siempre. */ public void RechleedEdExecution (runnable r, threadpoolexecutor e) {throw new DechRechEdExecutionException (); // Lanzamiento de excepción}}ThreadPoolExecutor.DiscardPolicy: significa que la tarea se rechaza pero no se realizan acciones. El código fuente es el siguiente:
Public static class Discardpolicy implementa rechazarexecutionHandler { /*** crea una <tt> descartepolicy </tt>. * / public DiscardPolicy () {} / *** no hace nada, que tiene el efecto de descartar la tarea r. * @param r La tarea ejecutable solicitada para ser ejecutada * @param e el ejecutor que intenta ejecutar esta tarea */ public void rechazadoxecution (runnable r, threadpoolexecutor e) {} // rechazar directamente, pero no hacer nada}ThreadPoolExecutor.CallerrunSpolicy: indica que la tarea se rechaza y la tarea se ejecuta directamente en el hilo de la persona que llama. El código fuente es el siguiente:
La clase estática pública CallerrunSpolicy implementa RechleDeDeCutionHandler { /*** crea una <tt> CallerrunSpolicy </tt>. * / public CallerrunSpolicy () {} / ** * Ejecuta la tarea R en el hilo de la persona que llama, a menos que el ejecutor * se haya cerrado, en cuyo caso la tarea se descarta. * @param r La tarea ejecutable solicitada para ser ejecutada * @param e El ejecutor que intenta ejecutar esta tarea */ public void rechazadoxecution (runnable r, threadpoolexecutor e) {if (! e.isshutdown ()) {r.run (); // ejecutar tareas directamente}}}ThreadPoolExecutor.DiscardoldestPolicy: significa que la primera tarea en la cola de tareas se descarta primero, y luego la tarea se agrega a la cola. El código fuente es el siguiente:
La clase estática pública DecarDOdestPolicy implementa RechleDeDExecutionHandler { /*** crea una <tt> DecarDOdSpolicy </tt> para el ejecutor dado. */ public DecarDOndSpolicy () {} public void rechazadoxecution (runnable r, threadpoolexecutor e) {if (! e.isshutdown ()) {e.getqueue (). poll (); // Deseche la primera tarea en la cola E.Execute (r); // ejecutar una nueva tarea}}}Cuando las tareas lleguen continuamente, se encuestará una tarea de la cola y luego se ejecutará una nueva tarea.
Resumir
Lo anterior es una explicación detallada del propio grupo de hilos del JDK introducido por el editor. Espero que sea útil para todos. Si tiene alguna pregunta, déjame un mensaje y el editor responderá a todos a tiempo. ¡Muchas gracias por su apoyo al sitio web de Wulin.com!