Una explicación detallada del grupo de subprocesos | Equipo técnico de JD Cloud

1. Hilo

1. ¿Qué es un hilo?

Thread es la unidad más pequeña que el sistema operativo puede realizar en la programación de cálculos. Está incluido en el proceso y es la unidad operativa real en el proceso. Un subproceso se refiere a un único flujo de control secuencial en un proceso. Se pueden ejecutar varios subprocesos al mismo tiempo en un proceso y cada subproceso realiza diferentes tareas en paralelo.

2. Cómo crear un hilo

2.1 Creando hilos en JAVA

/**
 * 继承Thread类,重写run方法
 */
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("myThread..." + Thread.currentThread().getName());
} }

/**
 * 实现Runnable接口,实现run方法 
 */
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("MyRunnable..." + Thread.currentThread().getName());
} }

/**
 * 实现Callable接口,指定返回类型,实现call方法
 */
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "MyCallable..." + Thread.currentThread().getName();
} }

2.2 Pruébalo

public static void main(String[] args) throws Exception {
    MyThread thread = new MyThread();
    thread.run();   //myThread...main
    thread.start(); //myThread...Thread-0
    
    MyRunnable myRunnable = new MyRunnable();
    Thread thread1 = new Thread(myRunnable);
    myRunnable.run();   //MyRunnable...main
    thread1.start();    //MyRunnable...Thread-1
    
    MyCallable myCallable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<>(myCallable);
    Thread thread2 = new Thread(futureTask);
    thread2.start();
    System.out.println(myCallable.call());  //MyCallable...main
    System.out.println(futureTask.get());   //MyCallable...Thread-2

} 

2.3 Problema

Dado que hemos creado un hilo, ¿por qué los resultados son diferentes cuando llamamos al método directamente y cuando llamamos al método start()? ¿New Thread() realmente crea un hilo?

2.4 Análisis de problemas

Cuando llamamos al método directamente, podemos ver que es el hilo principal de ejecución, y llamar al método start() inicia un nuevo hilo, lo que significa que new Thread() no crea un hilo, sino que crea un hilo en el inicio. ().

Entonces echemos un vistazo al método start() de la clase Thread:

class Thread implements Runnable { //Thread类实现了Runnalbe接口,实现了run()方法 
    
    private Runnable target;

    public synchronized void start() {
        ...

        boolean started = false;
        try {
            start0(); //可以看到,start()方法真实的调用时start0()方法 
            started = true;
        } finally {
            ...     
        } 
    }
    
    private native void start0();  //start0()是一个native方法,由JVM调用底层操作系统,开启一个线程,由操作系统过统一调度 

    @Override
    public void run() {
        if (target != null) {
             target.run(); //操作系统在执行新开启的线程时,回调Runnable接口的run()方法,执行我们预设的线程任务

        } 
     } 
} 

2.5 Resumen

1. JAVA no puede crear subprocesos directamente para realizar tareas, sino que llama al sistema operativo para iniciar el subproceso creando un objeto Thread, y el sistema operativo vuelve a llamar al método run () de la interfaz Runnable para realizar la tarea;
2. La forma de implementar Runnable separa las tareas de devolución de llamada que el hilo realmente realiza y desacopla el inicio del hilo de las tareas de devolución de llamada;
3. Implementar Callable: el modo Futuro no solo desacopla el inicio del hilo de la tarea de devolución de llamada, sino que también obtiene el resultado de la ejecución una vez completada la ejecución;

2. Subprocesos múltiples

1. ¿Qué es el subproceso múltiple?

Multithreading se refiere a la tecnología que permite la ejecución simultánea de múltiples subprocesos desde software o hardware. El mismo hilo solo puede procesar una tarea antes de procesar la siguiente. A veces necesitamos procesar varias tareas al mismo tiempo. En este caso, necesitamos crear varios hilos para procesar las tareas al mismo tiempo.

2. ¿Cuáles son los beneficios del subproceso múltiple?

2.1 Procesamiento en serie

public static void main(String[] args) throws Exception {
    System.out.println("start...");
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5; i++) {
        Thread.sleep(2000);  //每个任务执行2秒 
        System.out.println("task done..."); //处理执行结果
    }
    long end = System.currentTimeMillis();
    System.out.println("end...,time = "  + (end - start));
}
//执行结果
start...
task done...
task done...
task done...
task done...
task done... end...,time = 10043

2.2 Procesamiento paralelo

public static void main(String[] args) throws Exception {
    System.out.println("start...");
    long start = System.currentTimeMillis();
    List<Future> list = new ArrayList<>();

    for (int i = 0; i < 5; i++) {
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000); //每个任务执行2秒 
                return "task done...";
            }

        };
        FutureTask task = new FutureTask(callable);
        list.add(task);
        new Thread(task).start();

    }
    
    list.forEach(future -> {
        try { 
            System.out.println(future.get()); //处理执行结果 } catch (Exception e) {
         } 
    });
    
    long end = System.currentTimeMillis();
    System.out.println("end...,time = " + (end - start));

} 
//执行结果
 start...
 task done...
 task done...
 task done...
 task done...
 task done... end...,time = 2005 

2.3 Resumen

1. Los subprocesos múltiples pueden dividir una tarea en varias subtareas. Se pueden ejecutar varias subtareas al mismo tiempo. Cada subtarea es un subproceso.
2. El subproceso múltiple consiste en completar múltiples tareas simultáneamente, no para mejorar la eficiencia operativa, sino para mejorar la eficiencia del uso de recursos para mejorar la eficiencia del sistema.

2.4 Problemas con subprocesos múltiples

Podemos ver en el ejemplo anterior que si creamos un hilo para cada tarea, si hay muchas tareas, crearemos una gran cantidad de hilos, lo que puede provocar el agotamiento de los recursos del sistema. Al mismo tiempo, sabemos que la ejecución de subprocesos necesita aprovechar los recursos de la CPU. Si hay demasiados subprocesos, se dedicará mucho tiempo a la sobrecarga de cambio de subprocesos.

Además, cada tarea requiere la creación de un hilo, y la creación de un hilo requiere llamar al método subyacente del sistema operativo, lo cual es costoso y el hilo se recicla una vez completado. Cuando se necesita una gran cantidad de subprocesos, crear subprocesos lleva mucho tiempo.

3. Grupo de subprocesos

1. Cómo diseñar un grupo de subprocesos

Dado que el desarrollo de subprocesos múltiples tiene algunos de los problemas anteriores, ¿podemos diseñar algo para evitarlos? ¡Por supuesto! El grupo de subprocesos nació para resolver estos problemas. Entonces, ¿cómo deberíamos diseñar un grupo de subprocesos para resolver estos problemas? En otras palabras, ¿qué funciones debería tener un grupo de subprocesos?

1.1 Funciones básicas del grupo de subprocesos

1. Los subprocesos múltiples crearán una gran cantidad de subprocesos y agotarán los recursos. El grupo de subprocesos debe limitar la cantidad de subprocesos para garantizar que los recursos del sistema no se agoten;
2. Cada vez que se crea un nuevo hilo, aumentará el costo de creación. El grupo de hilos debe reducir la creación de hilos e intentar reutilizar los hilos ya creados;

1.2 El grupo de subprocesos enfrenta problemas

1. Sabemos que los hilos se reciclarán después de completar sus tareas, entonces, ¿cómo reutilizamos los hilos?
2. Hemos especificado el número máximo de subprocesos. Cuando el número de tareas excede el número de subprocesos, ¿cómo debemos manejarlo?

1.3 La innovación viene de la vida

Primero supongamos un escenario: supongamos que somos los gerentes de una empresa de logística. Los bienes a entregar son nuestras tareas y los camiones son nuestras herramientas de entrega. Por supuesto, no podemos preparar tantos camiones como bienes hay. Entonces, cuando los clientes continúan entregándonos productos, ¿cómo debemos gestionarlos para que la empresa pueda funcionar mejor?

1. Cuando llegó la mercancía todavía no teníamos camiones, tuvimos que comprar un camión por cada lote de mercancías a transportar;
2. Cuando se complete el transporte del camión y aún no haya llegado el siguiente lote de mercancías, el camión se estacionará en el almacén y podrá ser transportado inmediatamente cuando llegue la mercancía;
3. Cuando tengamos una cierta cantidad de autos y creamos que son suficientes, ya no compraremos autos. Si llegan nuevos productos en este momento, primero los pondremos en el almacén y esperaremos a que lleguen los autos. volver para la entrega;
4. Cuando llega la gran venta del 618, hay demasiadas mercancías para entregar, los autos están en la carretera y los almacenes están llenos. ¿Qué debemos hacer? Elegimos alquilar temporalmente algunos autos para ayudar con la entrega y mejorar el eficiencia de entrega;
5. Sin embargo, todavía hay demasiadas mercancías. Hemos añadido camiones temporales y todavía no pueden entregarlas. En este momento, no nos queda más remedio que hacer que los clientes hagan cola o simplemente negarse a aceptar las mercancías;
6. Una vez completada con éxito la gran venta y entregados los bienes acumulados, para reducir costos, devolveremos todos los autos alquilados temporalmente;

1.4 La tecnología proviene de la innovación

Según el escenario anterior, la empresa de logística es nuestro grupo de subprocesos, los productos son nuestras tareas de subprocesos y los camiones son nuestros subprocesos. La forma en que diseñamos el proceso de gestión de camiones de la empresa debería ser la forma en que diseñamos el proceso de gestión del grupo de subprocesos.

1. Cuando llega una tarea y aún no tenemos un hilo, debemos crear un hilo para ejecutar la tarea;
2. Cuando se completa la tarea del subproceso, el subproceso no se libera y espera a que entre la siguiente tarea y luego se ejecute;
3. Cuando el número de subprocesos creados alcanza una cierta cantidad, almacenamos nuevas tareas y esperamos a que se ejecuten los subprocesos inactivos, lo que requiere que el grupo de subprocesos tenga un contenedor para almacenar tareas;
4. Cuando el contenedor esté lleno, debemos agregar algunos subprocesos temporales para mejorar la eficiencia del procesamiento;
5. Cuando una tarea no se puede procesar después de agregar un hilo temporal, la tarea debe rechazarse;
6. Cuando se completen todas las tareas, los subprocesos temporales deben liberarse para evitar sobrecargas innecesarias;

2. Análisis detallado del grupo de subprocesos.

Arriba, hablamos sobre cómo diseñar un grupo de subprocesos, ahora veamos cómo lo diseñó el maestro;

2.1 ¿Cómo se diseña el grupo de subprocesos en JAVA?

2.1.1 Diseño del grupo de subprocesos

Eche un vistazo a las propiedades del grupo de subprocesos para comprender el diseño del grupo de subprocesos.

public class ThreadPoolExecutor extends AbstractExecutorService {

    //线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量 
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 
    
    //值为29,用来表示偏移量
     private static final int COUNT_BITS = Integer.SIZE - 3; 

    //线程池的最大容量
     private static final int CAPACITY = (1 << COUNT_BITS) - 1; 

    //线程池的运行状态,总共有5个状态,用高3位来表示 
    private static final int RUNNING = -1 << COUNT_BITS;  //接受新任务并处理阻塞队列中的任务 

    private static final int SHUTDOWN = 0 << COUNT_BITS;  //不接受新任务但会处理阻塞队列中的任务  

    private static final int STOP = 1 << COUNT_BITS;  //不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务

    private static final int TIDYING = 2 << COUNT_BITS;  //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法 

    private static final int TERMINATED =  3 << COUNT_BITS;  // terminated()方法已经执行结束

    //任务缓存队列,用来存放等待执行的任务
    private final BlockingQueue<Runnable> workQueue; 

    //全局锁,对线程池状态等属性修改时需要使用这个锁
    private final ReentrantLock mainLock = new ReentrantLock(); 

    //线程池中工作线程的集合,访问和修改需要持有全局锁
    private final HashSet<Worker> workers = new HashSet<Worker>(); 

    // 终止条件
    private final Condition termination = mainLock.newCondition(); 

    //线程池中曾经出现过的最大线程数 
    private int largestPoolSize; 
    
    //已完成任务的数量
    private long completedTaskCount; 
    
    //线程工厂
    private volatile ThreadFactory threadFactory; 
    
    //任务拒绝策略
    private volatile RejectedExecutionHandler handler; 

    //线程存活时间
    private volatile long keepAliveTime; 

    //是否允许核心线程超时
    private volatile boolean allowCoreThreadTimeOut; 

    //核心池大小,若allowCoreThreadTimeOut被设置,核心线程全部空闲超时被回收的情况下会为0 
    private volatile int corePoolSize; 

    //最大池大小,不得超过CAPACITY
    private volatile int maximumPoolSize; 
    
    //默认的任务拒绝策略
    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

    //运行权限相关
    private static final RuntimePermission shutdownPerm = 
        new RuntimePermission("modifyThread");

    ... 
} 

Para resumir: del diseño del grupo de subprocesos anterior, se puede ver que la función del grupo de subprocesos aún es muy completa.

1. Proporciona gestión de la creación, el número y el tiempo de supervivencia de los subprocesos;
2. Proporciona gestión de la transferencia del estado del grupo de subprocesos;
3. Proporciona varios contenedores para el almacenamiento en caché de tareas;
4. Proporciona un mecanismo para manejar tareas redundantes;
5. Proporciona funciones estadísticas simples;

2.1.2 Constructor de grupo de subprocesos

//构造函数
 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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
} 

Para resumir:

1. El constructor nos dice cómo usar el grupo de subprocesos y qué características del grupo de subprocesos podemos controlar;

2.1.3 Ejecución del grupo de subprocesos

2.1.3.1 Método de enviar tarea

ejecución pública nula (comando ejecutable);
Envío futuro<?> (tarea ejecutable);
Envío futuro (tarea ejecutable, resultado T);
Envío futuro (tarea invocable);
public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
}

Puede ver que el método subyacente del método de envío también llama al método de ejecución, por lo que aquí solo analizamos el método de ejecución;

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        //第一步:创建核心线程
        if (workerCountOf(c) < corePoolSize) {  //worker数量小于corePoolSize
            if (addWorker(command, true))       //创建worker
                return;
            c = ctl.get();
        }
        //第二步:加入缓存队列
        if (isRunning(c) && workQueue.offer(command)) { //线程池处于RUNNING状态,将任务加入workQueue任务缓存队列
            int recheck = ctl.get();    
            if (! isRunning(recheck) && remove(command))    //双重检查,若线程池状态关闭了,移除任务
                reject(command);
            else if (workerCountOf(recheck) == 0)       //线程池状态正常,但是没有线程了,创建worker
                addWorker(null, false);
        }
        //第三步:创建临时线程
        else if (!addWorker(command, false))
            reject(command);
    }

Para resumir: las funciones principales del método ejecutar():

1. Si la cantidad de subprocesos principales es insuficiente, cree subprocesos principales;
2. Cuando el subproceso principal esté lleno, se agregará a la cola de caché;
3. Agregue subprocesos no centrales cuando la cola de caché esté llena;
4. Si los subprocesos no principales también están llenos, la tarea será rechazada;

2.1.3.2 Crear hilos

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);//等价于:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
            //线程池已关闭,并且无需执行缓存队列中的任务,则不创建
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))  //CAS增加线程数
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }//上面的流程走完,就可以真实开始创建线程了
        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());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;
    }

Resumen: Las funciones principales del método addWorker();

1. Aumentar el número de subprocesos;
2. Cree una instancia de trabajador de subprocesos y únase al grupo de subprocesos;
3. Únase y complete el hilo de apertura;
4. Si el inicio falla, el proceso adicional se revertirá;

2.1.3.3 Implementación de subprocesos de trabajo

    private final class Worker  //Worker类是ThreadPoolExecutor的内部类
        extends AbstractQueuedSynchronizer  
        implements Runnable
    {
        
        final Thread thread;    //持有实际线程
        Runnable firstTask;     //worker所对应的第一个任务,可能为空
        volatile long completedTasks;   //记录执行任务数Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        
        public void run() {
            runWorker(this);    //当前线程调用ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
        }...继承AQS,实现了不可重入锁...
    }

Resumen: las funciones principales del hilo de trabajo Clase de trabajador;

1. Esta clase contiene un hilo de trabajo y procesa continuamente las nuevas tareas que obtiene. El hilo que contiene es un hilo reutilizable;
2. Esta clase puede considerarse como una clase de adaptación: en el método run (), en realidad se llama al método runWorker () para obtener continuamente nuevas tareas y completar la reutilización de subprocesos;

2.1.3.4 Reutilización de hilos

    final void runWorker(Worker w) {    //ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
        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);    //执行任务前的Hook方法,可自定义
                    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); //执行任务后的Hook方法,可自定义
                    }
                } finally {
                    task = null;    //执行完成后,将当前线程中的任务制空,准备执行下一个任务
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);    //线程执行完成后的清理工作
        }
    }

Resumen: Las funciones principales del método runWorker();

1. Realizar un bucle para obtener nuevas tareas de la cola de caché hasta que no queden tareas;
2. Utilice el hilo mantenido por el trabajador para ejecutar la tarea;
3. Limpiar después de completar todas las tareas;

2.1.3.5 Obtener tareas a ejecutar de la cola

    private Runnable getTask() {
        boolean timedOut = false;   //标识当前线程是否超时未能获取到task对象for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);// Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }int wc = workerCountOf(c);// Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))      //若线程存活时间超时,则CAS减去线程数量
                    return null;
                continue;
            }try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :   //允许超时回收则阻塞等待
                    workQueue.take();                                   //不允许则直接获取,没有就返回null
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

Resumen: Las funciones principales del método getTask();

1. Obtenga realmente las tareas que se ejecutarán en la cola de caché;
2. Aquí, administre si el hilo quiere bloquear y esperar, y controle la cantidad de hilos;

2.1.3.6 Trabajos de limpieza

  private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();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)) {    //到这里说明线程池没有终止
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);     //异常终止线程的话,需要在常见一个线程
        }
    }

Resumen: Las funciones principales del método ProcessWorkerExit();

1. Completar realmente el reciclaje de los subprocesos del grupo de subprocesos;
2. Llame para intentar finalizar el grupo de subprocesos;
3. Garantizar el funcionamiento normal del grupo de subprocesos;

2.1.3.7 Intente finalizar el grupo de subprocesos

    final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            
            //若线程池正在执行、线程池已终止、线程池还需要执行缓存队列中的任务时,返回
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
                
            //执行到这里,线程池为SHUTDOWN且无待执行任务 或 STOP 状态
            if (workerCountOf(c) != 0) {
                interruptIdleWorkers(ONLY_ONE);     //只中断一个线程
                return;
            }//执行到这里,线程池已经没有可用线程了,可以终止了
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {  //CAS设置线程池终止
                    try {
                        terminated();   //执行钩子方法
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));  //这里将线程池设为终态
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

Resumen: Las funciones principales del método tryTerminate();

1. Intento real de terminar el grupo de subprocesos;
2. Si la terminación es exitosa, se llama al método de enlace y el grupo de subprocesos se establece en el estado final.

2.2 Resumen del grupo de subprocesos JAVA

Del análisis detallado anterior del grupo de subprocesos JAVA, podemos ver que aunque el proceso parece complicado, en realidad hay muchos contenidos que incluyen verificación de duplicación de estado, garantía de seguridad de subprocesos, etc. Sus funciones principales son similares al diseño que propusimos anteriormente. las funciones son las mismas, pero se agregan algunas extensiones adicionales.Organicemos brevemente las funciones del grupo de subprocesos;

2.2.1 Funciones principales

1. Gestión del número de hilos y del tiempo de supervivencia;
2. Función de almacenamiento de tareas a procesar;
3. Función del mecanismo de reutilización de hilos;
4. Función de rechazo de tareas excesivas;
 

2.2.2 Funciones ampliadas

1. Función de estadísticas de resultados de ejecución simple;
2. Proporcionar un mecanismo de manejo de excepciones de ejecución de subprocesos;
3. Personalizar los procesos de pre y post procesamiento;
4. Proporcionar personalización de los métodos de creación de subprocesos;

2.2.3 Resumen del proceso

Del análisis anterior del proceso de envío de tareas del grupo de subprocesos JAVA, podemos ver que el proceso simple de ejecución del grupo de subprocesos se muestra en la siguiente figura;



2.3 Uso del grupo de subprocesos JAVA

Uso básico del grupo de subprocesos para verificar el proceso anterior:

   public static void main(String[] args) throws Exception {
        
        //创建线程池
       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
               5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue(5));
        
        //加入4个任务,小于核心线程,应该只有4个核心线程,队列为0
        for (int i = 0; i < 4; i++) {
            threadPoolExecutor.submit(new MyRunnable());
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 4
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
        
        //再加4个任务,超过核心线程,但是没有超过核心线程 + 缓存队列容量,应该5个核心线程,队列为3
        for (int i = 0; i < 4; i++) {
            threadPoolExecutor.submit(new MyRunnable());
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 3
        
        //再加4个任务,队列满了,应该5个热核心线程,队列5个,非核心线程2个
        for (int i = 0; i < 4; i++) {
            threadPoolExecutor.submit(new MyRunnable());
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 7
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
        
        //再加4个任务,核心线程满了,应该5个热核心线程,队列5个,非核心线程5个,最后一个拒绝
        for (int i = 0; i < 4; i++) {
            try {
                threadPoolExecutor.submit(new MyRunnable());
            } catch (Exception e) {
                e.printStackTrace();    //java.util.concurrent.RejectedExecutionException
            }
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 10
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
        System.out.println(threadPoolExecutor.getTaskCount());  //共执行15个任务
        
        //执行完成,休眠15秒,非核心线程释放,应该5个核心线程,队列为0
        Thread.sleep(1500);
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
        
        //关闭线程池
        threadPoolExecutor.shutdown();
    }

Autor: Qin Haoran, JD Retail 

Fuente: Comunidad de desarrolladores de JD Cloud Indique la fuente al reimprimir

Multado con 200 yuanes y más de 1 millón de yuanes confiscados You Yuxi: La importancia de los documentos chinos de alta calidad El servidor de migración de núcleo duro de Musk Solon para JDK 21, ¡los hilos virtuales son increíbles! ! ! El control de congestión de TCP salva Internet Flutter para OpenHarmony está aquí El período LTS del kernel de Linux se restaurará de 6 años a 2 años Go 1.22 solucionará el error de la variable del bucle for Svelte construyó una "nueva rueda" - runas Google celebra su 25 aniversario
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/10112678
Recomendado
Clasificación