Aprendizaje de subprocesos múltiples y alta concurrencia: análisis de código fuente de ThreadPoolExecutor

prefacio

El grupo de subprocesos se usa ampliamente en el trabajo, y aprender su código fuente puede comprender mejor las ideas relacionadas con la concurrencia.

texto

Análisis de código fuente——Atributos básicos

// ctl=11100000  00000000  00000000  00000000
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//COUNT_BITS=29
private static final int COUNT_BITS = Integer.SIZE - 3;
//(1 << COUNT_BITS)     00100000  00000000  00000000  00000000
//(1 << COUNT_BITS)-1   00011111 11111111	11111111 11111111	
private static final int CAPACITY   = (1 << COUNT_BITS)  - 1;

//大小顺序为:RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED
// runState is stored in the high-order bits
// RUNNING= 11100000  00000000  00000000  00000000
private static final int RUNNING    = -1 << COUNT_BITS;
// SHUTDOWN = 00000000	00000000 00000000 00000000	
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// STOP= 00100000  00000000  00000000  00000000
private static final int STOP       =  1 << COUNT_BITS;
// TIDYING= 01000000  00000000  00000000  00000000
private static final int TIDYING    =  2 << COUNT_BITS;
// TERMINATED= 01100000  00000000  00000000  00000000
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
//最终值大小取决于高三位
private static int runStateOf(int c)     {
    
     return c & ~CAPACITY; }
//最终值大小取决于低29位
private static int workerCountOf(int c)  {
    
     return c & CAPACITY; }
private static int ctlOf(int rs, int wc) {
    
     return rs | wc; }

Análisis de código fuente——ejecutar

public void execute(Runnable command) {
    
    
        if (command == null)
            throw new NullPointerException();
		
        int c = ctl.get();
    	//判断工作线程数量是否小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
    
    
            //添加为核心线程,true参数代表是添加一个核心线程
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
    	//到这里证明当前工作线程超过了核心线程数
    	//判断如果线程池是处于运行状态,则将任务添加到工作队列中
        if (isRunning(c) && workQueue.offer(command)) {
    
    
            //重新检查
            int recheck = ctl.get();
            //如果线程池不是运行状态,则将任务从工作队列中移除掉,并执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //判断当前的工作线程是否为0,如果为0创建一个新的工作线程用来消费处理队列中的任务
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
    	//走到这里并且能添加成功,证明此时的队列已经满了
    	//添加失败则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

Resumen del proceso:

1. Si el subproceso de trabajo inactivo actual es menor que la cantidad de subprocesos principales, cree un subproceso principal y ejecute la tarea directamente

2. Si el subproceso de trabajo inactivo actual es mayor que la cantidad de subprocesos principales, el grupo de subprocesos está en estado de ejecución y la tarea se coloca en la cola, esperando el consumo.

3. Repita la verificación para determinar si el estado del grupo de subprocesos se está ejecutando. De lo contrario, elimine las tareas agregadas a la cola anterior.

4. Si el subproceso de trabajo inactivo actual es 0, cree un subproceso de trabajo no central. Después de la creación, el subproceso de trabajo consumirá tareas en la cola

5. Si la cola de tareas en el subproceso está llena, intente agregar un subproceso de trabajo no central para procesar directamente

Análisis de código fuente - addWorker

private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    //外层循环标记
    retry:
    for (;;) {
    
    
        int c = ctl.get();
        //计算此时线程池的运行状态
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        //这里换种写法好理解写
        // if(rs>=SHUTDOWN){
    
    
        //     这里控制除了SHUTDOWN之外的其它状态,都不能添加新的工作线程
        //     if(rs != SHUTDOWN){
    
    
        //         return false;
        //     }
        //	   能到这里证明此时线程池状态为SHUTDOWN
        //	   如果任务为空,而队列不为空这种情况代表是创建新的非核心工作线程来消费处理队列,如:addWorker(null, false);
        //     if(!(firstTask == null&&!workQueue.isEmpty()){
    
    
        //         return false;
        //     }
        //  }
        
        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;
            //使用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
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
    
    
        //创建工作线程,将任务放到工作线程中
        w = new Worker(firstTask);
        //thread中的Runnable就是Worker
        final Thread t = w.thread;
        if (t != null) {
    
    
            //加锁,这样可以防止多线程情况下,其它线程调用了shutdown()方法,shutdownNow()方法
            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());
				//如果线程池处于运行状态,或者线程池处于SHUTDOWN状态并且任务参数为null这种情况(创建非核心工作线程来消费队列)
                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) {
    
    
                //开启调用work的run方法,而run方法调用了runWorker(this);
                t.start();
                workerStarted = true;
            }
        }
    } finally {
    
    
        //工作线程启动失败,则将该工作线程从集合中移除,并将当前工作线程数量-1
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

Resumen del proceso:

1. Verifique si el estado actual del grupo de subprocesos está en estado de ejecución. Si no está en estado de ejecución, juzgue si está APAGADO, y el parámetro de tarea está vacío, y la cola no está vacía (para addWorker (null, falso). En este caso, se permite crear subprocesos de trabajo, ya que el estado del grupo de subprocesos cambia a CERRADO inmediatamente después de agregar la tarea a la cola. En este caso, si la cola todavía tiene tareas que necesitan para ser consumido, se puede iniciar un subproceso de trabajo más para ayudar a consumir;

2. Intente agregar 1 al número total de subprocesos de trabajo a través de CAS. Si la adición de 1 es exitosa, prueba que el subproceso se puede agregar correctamente (para lidiar con escenarios de subprocesos múltiples), de lo contrario, sigue girando e intenta agregar subprocesos de trabajo

3. Cree un subproceso de trabajo y configure la tarea en él; dado que el propio trabajador implementa la interfaz Runnable, su atributo de subproceso es envolverse en un subproceso

4. Inicie el método de ejecución del subproceso de trabajo. Dado que se llama a runWorker(this) en el método de ejecución, este método es el método central de la tarea del procesador real

Análisis de código fuente——runWorker

final void runWorker(Worker w) {
    
    
    	//获取当前的工作线程
        Thread wt = Thread.currentThread();
    	//获取任务
        Runnable task = w.firstTask;
    	//将任务置为空,因为后面执行完这个任务后,工作线程还会去处理队列中的任务
        w.firstTask = null;
    	//由于Worker继承了AQS,这里调用unlock(),实际是把AQS中的状态设置为0,此时调用shutdown或者shutdownNow方法时,可以获取到锁,将当前线程中断掉
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
    
    
            //1、当前有任务处理,执行execute、submit传入的任务
            //2、从队列中获得到任务
            while (task != null || (task = getTask()) != null) {
    
    
                //获取到任务进行锁后,由于是非重入锁,此时不能中断在执行中的工作线程;所以SHUTDOWN状态下是没办法中断非空闲线程的;空闲线程由于没有lock所以可以被中断
                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或者STOP后面的状态时,不会再执行任务了
                //Thread.interrupted()会返回当前线程的中断标志,并且清除中断标志
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      //如果是STOP及其后面的状态则为true,!wt.isInterrupted()判断中断标志是否为false
                      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;
                    //处理完任务的数量+1
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
    
    
            processWorkerExit(w, completedAbruptly);
        }
    }

Resumen del proceso

1. Al principio, el estado en AQS se establece en 0, y cuando se permite llamar al método shutdown o shutdownNow, se puede obtener el bloqueo y el subproceso actual se interrumpirá.

2. Si actualmente hay una tarea entrante, ejecútela directamente; de ​​lo contrario, vaya a la cola para obtenerla. Si no se puede obtener, se destruirá el subproceso de trabajo actual;

3. Si el subproceso no está en estado de interrupción, ejecute la tarea. Si la ejecución es exitosa, se acumulará la cantidad de tareas procesadas por el subproceso actual.

4. Finalmente, destruye el hilo.

Análisis de código fuente - getTask

private Runnable getTask() {
    
    
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
    
    
        //获取线程池状态
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        //如果线程池处于SHUTDOWN并且队列为空,则没有任务需要处理
        //如果线程池状态为STOP,TYDING,TERMINATED,则不需要在处理
        //以上两种情况会将当前的工作线程销毁掉,这里先将数量-1。在外层方法中的processWorkerExit进行销毁
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            decrementWorkerCount();
            return null;
        }
		//统计当前工作线程数量
        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //timed为true时只有两种可能,一种是allowCoreThreadTimeOut=true允许核心线程在规定时间内获取不到任务时,进行销毁,此时的核心线程就跟非核心线程没有什么区别了
        //另一种情况是,核心线程不允许超时,也就是一直保持核心线程处于活跃状态,并且存在非核心线程(比较总工作线程数是否大于核心线程数)
        //核心工作核心和非核心工作线程没有什么区别,只是最终一直处于活跃状态就是核心线程。刚开始是核心线程后面可能就转为非核心线程,最终可能被销毁。
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		//1、超过最大线程池数量,直接销毁当前的工作线程
        //2、time为true代表允许超时销毁,而timeout=true代表获取任务超时,此时会判断工作线程数量是否大于1,如果<=1时,任务队列必须为空。这样判断的目的是为了保证将目前的工作线程销毁后,还存在线程可以处理任务,而如果任务都没有了,就不需要工作线程的存在了。
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
    
    
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
    
    
            //如果允许超时,则keepAliveTime时间内获取不到任务时,跳出来
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            //设置超时标识
            timedOut = true;
        } catch (InterruptedException retry) {
    
    
            timedOut = false;
        }
    }
}

Resumen del proceso:

1. Si el grupo de subprocesos está en CIERRE y la cola está vacía, no hay ninguna tarea que procesar y el subproceso de trabajo actual se puede destruir en este momento, o el estado del grupo de subprocesos es DETENER, TYDING, TERMINADO, entonces no hay no hay necesidad de procesar

2. Determine si se permite que el subproceso de trabajo actual agote el tiempo de espera, hay dos casos, uno es allowCoreThreadTimeOut=true permite que el subproceso principal establezca el tiempo de espera cuando la cola está adquiriendo tareas y lo destruye después del tiempo de espera. el subproceso principal no es diferente del subproceso no central ¿Cuál es la diferencia, porque el subproceso principal siempre está activo, incluso si no hay tareas en la cola, esperará; el segundo es que el subproceso principal no puede cronometrar y hay subprocesos no centrales (comparando si el número total de subprocesos de trabajo es mayor que el número de subprocesos principales), entonces se permite el tiempo de espera. El subproceso de trabajo no central establece el período de tiempo de espera al adquirir la cola. Cuando expira, será destruido;

Análisis de código fuente——processWorkerExit

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    
    //如果线程异常,则completedAbruptly=true,此时将工作线程数-1
    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();
    //如果线程池状态为RUNNING或SHUTDOWN
    if (runStateLessThan(c, STOP)) {
    
    
        if (!completedAbruptly) {
    
    
            
        	// min表示最小线程数,若allowCoreThreadTimeOut为true表示设置了允许核心线程数超时,则最小核心线程数为0,否则就是corePoolSize值
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            //如果队列中还有任务需要处理,则将最小核心数设置为1,保证有线程可以进行处理
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        //创建工作线程
        addWorker(null, false);
    }
}

Resumen del proceso:

1. Si completeAbruptly=true, prueba que ocurrió una excepción, entonces el número total de subprocesos de trabajo será -1, porque si completeAbruptly=false, el proceso se ejecuta normalmente y el subproceso actual se destruye porque el subproceso no puede encontrar la tarea a ejecutar, mientras que el número de subprocesos -1 paso se ha completado en el paso getTask();

2. Resuma el número de tareas de procesamiento completadas

3. Interrumpa los subprocesos de trabajo elegibles y establezca el estado final del grupo de subprocesos

4. Si el estado del grupo de subprocesos es EN EJECUCIÓN o APAGADO, y el proceso se ejecuta normalmente sin excepción, determine si se debe permitir que el subproceso principal se agote, porque si se permite que el subproceso principal se agote, entonces el subproceso principal está no es diferente del subproceso no central, y puede bloquearse porque no hay tarea Destruir, en este momento establezca el número mínimo de subprocesos en 0, de lo contrario, establezca el umbral del número de subprocesos principales;

5. Determine si hay tareas en la cola y, de ser así, reserve la cantidad mínima de subprocesos de trabajo calculados en el paso 4 para ejecutar las tareas de la cola.

Análisis del código fuente——tryTerminate

final void tryTerminate() {
    
    
    for (;;) {
    
    
        int c = ctl.get();
        //SHUTDOWN状态下队列没有任务了 或者  状态为STOP会往下走
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        //如果线程池存在工作线程,将工作线程中断
        if (workerCountOf(c) != 0) {
    
     // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
    
    
            //将线程池状态设置为TIDYING
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
    
    
                try {
    
    
                    //执行中断钩子函数
                    terminated();
                } finally {
    
    
                    //将线程池状态设置为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
    
    
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

Resumen del proceso:

1. Determine si el subproceso de trabajo debe interrumpirse

  • El grupo de subprocesos se está ejecutando y no es necesario interrumpirlo.
  • El grupo de subprocesos está en estado APAGADO, pero todavía hay tareas en la cola que no se han procesado, por lo que no es necesario interrumpir
  • Los estados TYDING y TEMINATED no necesitan ser interrumpidos, porque el estado STOP antes de entrar a estos dos estados ya ha realizado la operación de interrupción del hilo.

2. Si hay subprocesos de trabajo en el grupo de subprocesos, interrumpa los subprocesos de trabajo

3. Después de bloquear, modifique el estado del grupo de subprocesos para establecer el estado del grupo de subprocesos en ORDENAR

4. Después de ejecutar la función de enlace, establezca el estado del grupo de subprocesos en TERMINADO

Análisis del código fuente——apagado

public void shutdown() {
    
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        checkShutdownAccess();
        //设置线程池状态为SHUTDOWN
        advanceRunState(SHUTDOWN);
        //将所有工作线程中断标志设置为true
        interruptIdleWorkers();
        //尝试将工作线程打断
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
    
    
        mainLock.unlock();
    }
    //尝试中断,会中断空闲的线程因为空闲线程还没有lock
    tryTerminate();
}

Análisis del código fuente——shutdownNow

public List<Runnable> shutdownNow() {
    
    
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        
        checkShutdownAccess();
        //设置线程池状态为STOP
        advanceRunState(STOP);
        //中断所有线程
        interruptWorkers();
        //清空队列任务
        tasks = drainQueue();
    } finally {
    
    
        mainLock.unlock();
    }
    //尝试中断,会中断空闲的线程因为空闲线程还没有lock,而那些有lock的由于队列任务被清空,执行完任务后也会被销毁
    tryTerminate();
    return tasks;
}

Resumir

Sabemos por el código fuente anterior que los subprocesos centrales son esencialmente los mismos que los subprocesos no centrales, solo para mantener varios subprocesos activos y procesar tareas en la cola cuando hay menos tareas y la cantidad de subprocesos centrales está en estado activo, puede procesar las tareas de la cola en cualquier momento; pero hay un caso en el que el subproceso central también se destruirá, es decir, se establece el indicador "permitir tiempo de espera del subproceso central", y el subproceso central en este momento no es diferente del no central subproceso Después del tiempo de espera, se destruirá, pero si hay tareas en la cola, la cantidad mínima de grupos de subprocesos se establecerá en 1, y un subproceso en el grupo de subprocesos se puede usar para procesar tareas;

Por el código fuente, podemos saber que cuando se llama al método de apagado, el estado del grupo de subprocesos cambia al estado SHUTDOWN.En este momento, intentará interrumpir el subproceso en el estado inactivo, es decir, el subproceso sin lock; y los subprocesos que mantienen el bloqueo seguirán en Las tareas en la cola se destruyen hasta que se consuman; no se permite agregar nuevas tareas en el estado SHUTDOWN, pero se permiten las tareas que se agregaron a la cola ser ejecutado;

Después de llamar al método shutdownNow, el estado del grupo de subprocesos cambia a STOP, y todos los subprocesos inactivos se interrumpen en este momento y las tareas de la cola se borran. Y aquellos que todavía están en estado de ejecución se destruyen después de que se ejecutan y no pueden realizar la tarea.

Cuando no hay subprocesos en funcionamiento en todos los grupos de subprocesos y el estado es DETENER, cambie el estado a TYDING a través de CAS y cambie el estado del grupo de subprocesos a TERMINADO después de ejecutar la función de enlace

Supongo que te gusta

Origin blog.csdn.net/weixin_45031612/article/details/130756955
Recomendado
Clasificación