[Programación concurrente de Java] finalización de puntos de conocimiento relacionados con el grupo de subprocesos

¿Por qué utilizar el grupo de subprocesos?

Pooling technology: Reducir el consumo de recursos cada vez y mejorar la utilización de los recursos.

El grupo de subprocesos proporciona una forma de limitar y administrar los recursos (incluida la ejecución de una tarea). Cada grupo de subprocesos también mantiene algunas estadísticas básicas, como el número de tareas completadas.

Beneficios de usar el grupo de subprocesos:

  • Reducir el consumo de recursos . Reduzca el consumo causado por la creación y destrucción de subprocesos reutilizando los subprocesos creados.
  • Mejora la velocidad de respuesta . Cuando llega la tarea, la tarea se puede ejecutar inmediatamente sin esperar hasta que se cree el hilo.
  • Mejore la capacidad de administración de los hilos . Los subprocesos son recursos escasos. Si se crean de forma ilimitada, no solo consumirán recursos del sistema, sino que también reducirán la estabilidad del sistema. El grupo de subprocesos se puede utilizar para una asignación, ajuste y supervisión uniformes.

Visualización recomendada: Portal

¿El principio de realización del grupo de subprocesos?

ejecutar el código fuente del método

    public void execute(Runnable command) {
        // 如果任务为null,则抛出异常。        if (command == null)
            throw new NullPointerException();        // ctl 中保存的线程池当前的一些状态信息  AtomicInteger        int c = ctl.get();        //判断当前线程池中执行的任务数量是否小于corePoolSize        if (workerCountOf(c) < corePoolSize) {
            //如果小于,则通过addWorker新建一个线程,然后,启动该线程从而执行任务。            if (addWorker(command, true))
                return;
            c = ctl.get();        }        //通过 isRunning 方法判断线程池状态        //线程池处于 RUNNING 状态才会被并且队列可以加入任务        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();            // 再次获取线程池状态,如果线程池状态不是 RUNNING 状态就需要从任务队列中移除任务。            // 并尝试判断线程是否全部执行完毕。同时执行拒绝策略。            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果当前线程池为空就新创建一个线程并执行。            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }        //通过addWorker新建一个线程,并将任务(command)添加到该线程中;
        //然后,启动该线程从而执行任务。        //如果addWorker执行失败,则通过reject()执行相应的拒绝策略的内容。        else if (!addWorker(command, false))
            reject(command);
    }

Cuando el grupo de subprocesos crea un subproceso, encapsulará el subproceso en un subproceso de trabajo. Después de que el trabajador complete la tarea, obtendrá cíclicamente las tareas de la cola de trabajo para su ejecución.

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();        Runnable task = w.firstTask;        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {//循环执行任务
                w.lock();                //如果线程池正在停止,确保线程被中断
                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++;                    w.unlock();                }            }            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);        }    }

 

Puntos de conocimiento relacionados con el grupo de subprocesos de "Programación concurrente de Java"

 

  1. El grupo de subprocesos juzga si los subprocesos del grupo de subprocesos central [corePoolSize] están realizando tareas. Si no es así, cree un nuevo hilo de trabajo para realizar la tarea. Si todos los subprocesos del grupo de subprocesos principales están realizando tareas, ingrese al siguiente proceso.
  2. El grupo de subprocesos juzga si la cola de trabajo [BlockingQueue] está llena. Si la cola de trabajos no está llena, las tareas recién enviadas se almacenan en esta cola de trabajos. Si la cola de trabajo está llena, ingrese al siguiente proceso.
  3. El grupo de subprocesos juzga si todos los subprocesos del grupo de subprocesos [maximumPoolSize] están funcionando. Si no es así, cree un nuevo hilo de trabajo para realizar la tarea. Si está lleno, entréguelo a la estrategia de saturación [RejectedExecutionHandler.rejectedExecution ()] para manejar esta tarea.

 

Puntos de conocimiento relacionados con el grupo de subprocesos de "Programación concurrente de Java"

 

¿Cómo usar el grupo de subprocesos?

Análisis importante de ThreadPoolExecutor

Parámetros importantes del método de construcción.

Hay muchos parámetros de construcción del método ThreadPoolExecutor, podemos ver el más largo:

    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: el número de subprocesos principales define el número mínimo de subprocesos que pueden ejecutarse simultáneamente .
  • maximumPoolSize: cuando las tareas almacenadas en la cola alcanzan la capacidad de la cola , el número de subprocesos que se pueden ejecutar actualmente al mismo tiempo se convierte en el número máximo de subprocesos . [Si usa una cola ilimitada, este parámetro no tiene ningún efecto]
  • workQueue: cuando llega una nueva tarea, primero determinará si el número de subprocesos que se están ejecutando actualmente alcanza el número de subprocesos principales. Si alcanza el número de subprocesos principales, la nueva tarea se almacenará en la cola .
  • keepAliveTime: cuando el número de subprocesos en el grupo de subprocesos es mayor que corePoolSize, si no se envían nuevas tareas en este momento, los subprocesos fuera del subproceso principal no se destruirán inmediatamente, sino que esperarán hasta que el tiempo de espera supere a keepAliveTime antes de reciclarse y destruirse .
  • unidad: la unidad de tiempo de keepAliveTime.
  • threadFactory: se utiliza para configurar la fábrica para la creación de hilos. Puede establecer un nombre más significativo para cada hilo creado a través de la fábrica de hilos.
  • handler: estrategia de saturación Cuando el número de subprocesos que se ejecutan actualmente al mismo tiempo alcanza el número máximo de subprocesos [maximumPoolSize] y la cola está llena, se ejecutará la estrategia de saturación.

Uso simple del grupo de subprocesos

public class ThreadPoolTest {
    private static final int CORE_POOL_SIZE = 5; //核心线程数
    private static final int MAX_POOL_SIZE = 10; //最大线程数
    private static final int QUEUE_CAPACITY = 100; //任务队列的容量
    private static final Long KEEP_ALIVE_TIME = 1L; //等待时间
    public static void main(String[] args) {        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(                CORE_POOL_SIZE,                MAX_POOL_SIZE,                KEEP_ALIVE_TIME,                TimeUnit.SECONDS,                new ArrayBlockingQueue<>(QUEUE_CAPACITY),                new ThreadPoolExecutor.AbortPolicy());        for(int i = 0; i < 10 ; i ++){
            Runnable worker = new MyRunnable(""+ i); //创建任务
            threadPool.execute(worker); //通过execute提交
        }        threadPool.shutdown();        while(!threadPool.isTerminated()){
        }        System.out.println("Finished all threads");
    }}class MyRunnable implements Runnable {    private String command;    MyRunnable(String s) {
        this.command = s;
    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
        processCommand();        System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
    }    private void processCommand() {        try {            Thread.sleep(5000);
        } catch (InterruptedException e) {            e.printStackTrace();        }    }    @Override    public String toString() {        return this.command;
    }}

¿Qué son las colas de tareas?

  • ArrayBlockingQueue: una cola de bloqueo limitada basada en la estructura de la matriz, que ordena los elementos según el principio FIFO.
  • LinkedBlockingQueue: cola de bloqueo basada en la estructura de la lista vinculada, ordenando elementos por FIFO, el rendimiento suele ser mayor que ArrayBlockingQueue, Executors.newFixedThreadPool () usa esta cola.
  • SynchronousQueue: una cola de bloqueo que no almacena elementos. Cada operación de inserción debe esperar hasta que otro subproceso llame a la operación de eliminación; de lo contrario, la operación de inserción se ha bloqueado y el rendimiento suele ser mayor que LinkedBlockingQueue. Esta cola es utilizada por Executors.newCachedThreadPool () .
  • PriorityBlockingQueue: una cola de bloqueo infinita con prioridad.

¿Cuáles son las estrategias de saturación?

  • ThreadPoolExecutor.AbortPolicy : Lanza una RejectedExecutionException para rechazar el procesamiento de la nueva tarea. [Estrategia de saturación predeterminada]
  • ThreadPoolExecutor.CallerRunsPolicy [Proporcionar una cola escalable]: Llama para ejecutar tu propio hilo para ejecutar la tarea, es decir, ejecuta la tarea rechazada directamente en el hilo que llama al método de ejecución. Si el programa de ejecución está cerrado, la tarea será descartada . Por lo tanto, esta estrategia reducirá la velocidad de envío de nuevas tareas y afectará el desempeño general del programa. Si su aplicación puede tolerar este retraso y necesita que se ejecute cualquier solicitud de tarea, puede elegir esta estrategia. public voidjectedExecution (Runnable r, ThreadPoolExecutor e) {if (! e.isShutdown ()) {r.run ();}}
  • ThreadPoolExecutor.DiscardPolicy:  No procese nuevas tareas, simplemente deséchelas.
  • ThreadPoolExecutor.DiscardOldestPolicy:  esta política descartará la solicitud de tarea no procesada más antigua y ejecutará la tarea actual. public voidjectedExecution (Runnable r, ThreadPoolExecutor e) {if (! e.isShutdown ()) {e.getQueue (). poll (); e.execute (r);}}

Por supuesto, también puede personalizar la estrategia de rechazo según sus necesidades, y debe implementar RejectedExecutionHandler.

¿Cómo crear un grupo de subprocesos?

1. Utilice varios métodos de construcción de ThreadPoolExecutor.

2. Se pueden crear tres tipos de ThreadPoolExecutor a través de Executors, la clase de herramienta del marco Executor.

El grupo de subprocesos forzado en el "Manual de desarrollo de Java de Alibaba" no permite el uso de ejecutores para crearlo, pero utiliza el método ThreadPoolExecutor . Este método de procesamiento permite a los estudiantes escribir más claramente las reglas de funcionamiento del grupo de subprocesos y evitar el riesgo de agotamiento de recursos

Los inconvenientes de los Ejecutores que devuelven objetos de grupo de subprocesos son los siguientes:  FixedThreadPool y SingleThreadExecutor : La longitud de la cola permitida para las solicitudes es Integer.MAX_VALUE, que puede acumular una gran cantidad de solicitudes, lo que lleva a OOM. CachedThreadPool y ScheduledThreadPool  : la cantidad de subprocesos que se permite crear es Integer.MAX_VALUE, que puede crear una gran cantidad de subprocesos, lo que lleva a OOM.

¿La diferencia entre el método de ejecución y el método de envío?

  • El método execute () se usa para enviar tareas que no requieren un valor de retorno, por lo que es imposible determinar si el grupo de subprocesos ejecuta correctamente la tarea;
threadPool.execute(new Runnable() {
    @Override
    public void run() {
    }}); //通过execute提交
  • El método submit () se usa para enviar tareas que requieren valores devueltos. El grupo de subprocesos devolverá un objeto de tipo Future. Este objeto Future se puede usar para determinar si la tarea se ejecuta correctamente , y el valor de retorno se puede obtener mediante el método get () de Future. El método get () bloqueará el subproceso actual hasta que se complete la tarea, y usar get El método (tiempo de espera prolongado, unidad TimeUnit) bloqueará el hilo actual durante un período de tiempo y volverá inmediatamente. En este momento, es posible que la tarea no se complete.
Future<Object> future = threadPool.submit(hasReturnValueTask);
try{
    Object s = future.get();
}catch(InterruptedException e){
    //处理中断异常
}catch(ExecutionException e){
    //处理无法执行任务异常
}finally{
    threadPool.shutdown();
}

Si cree que este artículo es útil para usted, le puede gustar y seguirlo para respaldarlo, o puede seguir mi cuenta pública. Hay más artículos técnicos sobre productos secos e información relacionada para compartir, ¡todos aprenden y progresan juntos!

 

Supongo que te gusta

Origin blog.csdn.net/weixin_50205273/article/details/108682056
Recomendado
Clasificación