[Código fuente de JUC] Grupo de subprocesos: ThreadPoolExecutor (cuatro) análisis del código fuente del proceso de ejecución de tareas

Serie de grupos de hilos:

1.ejecutar ()

Entrada, seleccione la estrategia de ejecución, dividida en las siguientes tres situaciones:

  • Caso 1: Subproceso de trabajador <número de núcleos, cree un subproceso para realizar tareas
  • Caso 2: subproceso de trabajo> = número de núcleos y la cola de tareas no está llena, únase a la cola de tareas (esperando a que se ejecute el subproceso principal)
    • El grupo de subprocesos es anormal, elimine la tarea actual
    • Situación limitante: los subprocesos disponibles simplemente se reciclan cuando se ponen en cola y se crea un nuevo subproceso sin tareas
  • Situación 3: la cola de tareas está llena
    • La cola está llena && número de subprocesos <maxSize: crea un nuevo subproceso para procesar tareas
    • La cola está llena && número de subprocesos> = maxSize: use la clase RejectedExecutionHandler para rechazar la solicitud
public void execute(Runnable command) {
    
    
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get(); // 获取 ctl 
    // 情况一:工作的线程小于核心线程数,创建新的线程,成功返回,失败不抛异常
    if (workerCountOf(c) < corePoolSize) {
    
    
        if (addWorker(command, true))
            return;
        // 由于 addWorker -> runWorker -> getTask,所以线程池状态可能发生变化
        c = ctl.get();
    }
    // 情况二:工作的线程大于等于核心线程数且任务队列没满
    // 注:isRunning是校验线程池状态是否正常。另外,offer不阻塞而是返回t/f
    if (isRunning(c) && workQueue.offer(command)) {
    
    
        int recheck = ctl.get();
        // 如果线程池状态异常 尝试从队列中移除任务,可以移除的话就拒绝掉任务
        if (!isRunning(recheck) && remove(command))
            reject(command);
        // 发现可运行的线程数是 0,就初始化一个线程,这里是个极限情况,入队的时候,突然发现可用线程都被回收了
        else if (workerCountOf(recheck) == 0)
            // Runnable是空的,不会影响新增线程,但是线程在 start 的时候不会运行
            // Thread.run() 里面有判断
            addWorker(null, false);
    }
    // 情况三:队列满了,开启线程到 maxSize,如果失败直接拒绝(这段逻辑可以在addWorker方法中看到)
    else if (!addWorker(command, false))
        reject(command);
}

2.addWorker ()

Cree un trabajador y devuelva si el trabajador se inició correctamente. El proceso general es el siguiente:

  1. Verificación del estado del grupo de subprocesos
    • Si falla, devuelve falso. Hay dos razones:
      • Estado anormal del grupo de subprocesos: APAGADO, DETENER, PONER EN ORDEN, TERMINADO
      • Desbordamiento del número de subprocesos de trabajo: el número de subprocesos> = capacidad o en el caso de usar coreThread, el número de subprocesos> = coreSize o maxSize
    • Éxito: CAS aumenta workCount en uno
  2. Crear trabajador
    1. Cree dos variables de identificación: workerAdded, workerStarted
    2. Construya un trabajador, se creará un nuevo hilo a través del método newThread durante la construcción
    3. Bloquear, agregar el trabajador recién creado al contenedor (Conjunto) que administra el trabajador. Los bloqueos garantizan la seguridad de los subprocesos durante la simultaneidad
  3. Inicie el hilo en el trabajador, la lógica de llamada es: Thread # start () -> Worker # run () -> runWorker ()
// firstTask 不为空可以直接执行,为空执行不了,Thread.run()方法有判断,Runnable为空不执行
// core 为 true 表示线程最大新增个数是 coresize,false 表示最大新增个数是 maxsize
private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    
	// break retry 跳到retry处,且不再进入循环
	// continue retry 跳到retry处,且再次进入循环
    retry:
--------------------------------------------------------------------------------------------------------------    
    // 1.先是各种状态的校验
    for (;;) {
    
    
        int c = ctl.get();
        int rs = runStateOf(c); // 获取线程池状态
        
        // 1.1 校验线程池状态,rs>=0:SHUTDOWN,STOP,TIDYING,TERMINALED
        if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) 
            return false;

        for (;;) {
    
    
            int wc = workerCountOf(c); // 得到当前工作线程数,即worker数
            // 1.2 校验工作中的线程数大于等于容量,或者大于等于 coreSize or maxSize
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) // 如果core为true就判断是否大于coreSize,否则判断maxSize
                return false;
            // CAS修改 workerCount(+1)
            if (compareAndIncrementWorkerCount(c))
                // break 结束 retry 的 for 循环
                break retry;
            // 到这里可能是CAS失败了,重新获取 ctl
            c = ctl.get();  
            // 如果线程池状态被更改
            if (runStateOf(c) != rs)
                continue retry; // 跳转到retry位置,重新判断
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
--------------------------------------------------------------------------------------------------------------
	// 2.创建worker
	// 2.1 创建标识变量
    boolean workerStarted = false; // woker启动标识
    boolean workerAdded = false;  // woker成功加入worker容器标识
    Worker w = null;
    try {
    
    
    	// 2.2 构造worker。在worker的构造函数中会调用newThread方法创建一个Thread
    	// 注:由于Worker也实现了Runnable,所以在创建线程的时候是newThread(this)。这是一个巧妙的设计
        w = new Worker(firstTask);
        final Thread t = w.thread; // 获取worker中的线程
        // 2.3 将worker加入到worker容器(Set)
        if (t != null) {
    
    
            final ReentrantLock mainLock = this.mainLock; // 这个mainLock是一个成员变量,作用是控制对worker的操作
            // 加锁是因为,可能有多个线程同时要将worker放入worker容器
            mainLock.lock();
            try {
    
    
				// 获取到线程池状态rs
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN || // 如果线程池状态是 RUNNING
                    (rs == SHUTDOWN && firstTask == null)) {
    
     // 线程池是SHUTDOWN且要执行的任务为null
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 将当前woker加入到 HashSet<Worker> workers 中
                    workers.add(w); 
                    int s = workers.size(); // 获取到 workers 的大小,即现在有几个worker
                    // 如果worker数已经大于了最大线程池容量
                    if (s > largestPoolSize) 
                        largestPoolSize = s; // 将largestPoolSize设置为worker现在的书香
                    workerAdded = true; // 添加标志设置为成功
                }
            } finally {
    
    
                mainLock.unlock(); // 解锁
            }
--------------------------------------------------------------------------------------------------------------            
            // 3.启动如果woker中的线程。前提是worker已经添加成功
            if (workerAdded) {
    
    
                // 启动刚创建线程:Thread#start -> Worker#run -> runWorker
                t.start();
                workerStarted = true; // 线程启动标志置为true
            }
        }
    } finally {
    
    
    	// 如果线程启动失败
        if (! workerStarted) 
            addWorkerFailed(w);
    }
    // 返回线程是否启动成功
    return workerStarted;
}

3.runWorker ()

Primero obtenga la tarea y luego deje que el trabajador la realice. La lógica general del método es la siguiente:

  1. Hay dos formas de realizar la tarea.
    • firstTask: Tarea inicial del trabajador
    • getTask (): la tarea de la cola de tareas
  2. Bloquear para evitar que el hilo se lance a la tarea durante la ejecución
  3. Si el grupo de subprocesos está en STOP, interrumpa el subproceso actual
  4. Ejecutar antes de la función de gancho
  5. Ejecute la tarea, es decir, llame a task.run ()
  6. Ejecute la función de gancho posterior
  7. Elimina la tarea actual y suelta el bloqueo. mientras ejecuta la siguiente tarea

Una nota más aquí, mientras que el propósito es mantener el hilo actual para continuar realizando tareas, pero si el hilo no logra obtener la tarea (el método getTask se bloqueará y esperará), saldrá del ciclo, es decir, el hilo se reciclará al final de su vida útil.

final void runWorker(Worker w) {
    
    
    Thread wt = Thread.currentThread(); // 获取当前线程
    Runnable task = w.firstTask;  // 尝试获取创建worker时的firstTask
    
    w.firstTask = null; // 从这可以看出,只要firstTask执行过一次,就会一直被置为null
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    
    
    	// 1.获取任务:如果firstTask已经被执行过了,就从任务队列中获取
    	// 注:通过while维持了线程的存活,并不断获取任务取执行。若迟迟拿不到任务,就会退出while结束线程
        while (task != null || (task = getTask()) != null) {
    
    
            // 2.锁住 worker,防止worker在执行任务时被丢入另一个任务
            w.lock();
            // 3.判断线程池若处于 stop 中,但线程没有到达中断状态,帮助线程中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
    
    
                // 4.执行 before 钩子函数
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
    
    
                    // 5.同步执行任务
                    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 {
    
    
                    // 6.执行 after 钩子函数
                    // 如果这里抛出异常,会覆盖 catch 的异常,所以这里异常最好不要抛出来
                    afterExecute(task, thrown);
                }
            } finally {
    
    
                // 7.任务执行完成,删除任务,并计算解锁
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
    
    
        // 做一些抛出异常的善后工作
        processWorkerExit(w, completedAbruptly);
    }
}

4.getTask ()

Obtener tareas de la cola de bloqueo. Si la tarea no se recupera después de bloquear y esperar, devolverá un valor nulo, de modo que el hilo actual salga del bucle while en el método runWorker y se recicla, porque no queda nada por hacer y los recursos son desperdiciado. La estrategia de reciclaje específica está en el código fuente y el proceso general del método es el siguiente:

  1. El primer juicio para reciclar el hilo actual: grupo de hilos APAGADO, y la cola está vacía
  2. El segundo juicio es reciclar el hilo actual y cumplir cualquiera de las siguientes condiciones:
    1. wc> maximumPoolSize && wc> 1: el número de trabajadores existentes excede la capacidad máxima del grupo de subprocesos, y hay al menos un subproceso en el grupo de subprocesos después del reciclaje
    2. wc> maximumPoolSize && workQueue.isEmpty (): el número de trabajadores existentes excede la capacidad máxima del grupo de subprocesos y la cola de tareas está vacía
    3. timed && timedOut && wc> 1: Permitir que los subprocesos principales se reciclen o el número de subprocesos existentes excede el número de núcleos, y el subproceso actual ha agotado el tiempo de espera, y hay al menos un subproceso en el grupo de subprocesos después del reciclaje
    4. timed && timedOut && workQueue.isEmpty (): permite reciclar subprocesos centrales o el número de subprocesos existentes excede el número de núcleos, y el subproceso actual ha agotado el tiempo de espera y la cola de tareas está vacía
  3. Obtenga la tarea (toma o encuesta) de la cola de tareas, devuélvala si la obtiene y establezca el tiempo de espera (timedOut) en verdadero si no lo obtiene.
    Nota: Solo cuando el tiempo sea verdadero, se usará la encuesta y luego esperará para KeepAliveTime; de ​​lo contrario, se usará take all the time Espere

PD: En este caso, de nuevo, los subprocesos centrales y los subprocesos no centrales son solo conceptualmente diferentes. Todos en el código son iguales, todos son subprocesos ordinarios.

private Runnable getTask() {
    
    
	// 标识是否超时
	// 默认false,但如果下面自旋中 poll 在 keepAliveTime(线程存活时间) 没等到任务,就会将timedOut置为true
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
    
    
        int c = ctl.get();
        int rs = runStateOf(c); // 获取线程池状态
        
		// 1.第一次判断是否回当前收线程
        // 线程池关闭 && 队列为空,不需要在运行了,直接返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            decrementWorkerCount(); // workerCount--
            return null;
        }

        int wc = workerCountOf(c); // 获取worker个数
        // timed的作用是决定在阻塞队列中等任务时用 poll 还是 take
        // timed = 核心线程可以被灭亡(默认false) || 运行的线程数大于 coreSize 
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
	   
        // 2. 第二次判断是否回收当前线程。组合后分为4种情况
        if ((wc > maximumPoolSize || (timed && timedOut))  // woker大于线程池最大数量 || (timed && 当前线程已经超时)
            && (wc > 1 || workQueue.isEmpty())) {
    
     // woker大于1 || 任务队列为空
            // 通过CAS使workerCount--
            if (compareAndDecrementWorkerCount(c)) 
                return null;
            continue;
        }

        try {
    
    
        	// 3.从阻塞队列中获取任务。timed 决定了是使用 poll 还是 take
        	// keepAliveTime 是线程最大空闲时间,是构造线程池的入参
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // poll,超时了就返回
                workQueue.take(); // take,任务队列中没任务会阻塞等待
            // 如果在队列拿到了任务就返回
            if (r != null)
                return r;
            // 没拿到就将超时timedOut设置为true,表示此时队列没有数据
            timedOut = true;
        } catch (InterruptedException retry) {
    
    
            timedOut = false;
        }
    }
}

resumen

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_43935927/article/details/113965324
Recomendado
Clasificación