(14) grupo de subprocesos

Grupo de subprocesos

Un grupo de subprocesos es una colección de subprocesos. El grupo de subprocesos mantiene una cola y la persona que llama agrega tareas a la cola, mientras que los subprocesos en el grupo de subprocesos siguen sacando tareas de la cola para su ejecución.

La relación de herencia del grupo de subprocesos se muestra en la siguiente figura, donde ThreadPoolExecutor y ScheduledThreadPoolExecutor son implementaciones específicas.

ThreadPoolExecutor

El grupo de subprocesos básico puede enviar tareas sin valor de retorno y valor de retorno para su ejecución. El método de construcción sobrecargado tiene hasta siete parámetros, la proporción es el número de subprocesos principales (subprocesos residentes), el número máximo de subprocesos (cuando el subproceso principal está lleno, la cola de tareas también está llena, el número máximo de subprocesos de expansión) , tiempo de inactividad del subproceso no central, unidad de tiempo, cola de tareas de bloqueo, fábrica de producción de subprocesos, estrategia de saturación (utilizada para tratar con el subproceso central lleno, el subproceso máximo está lleno y la estrategia para nuevas tareas entrantes cuando la cola de tareas está llena , también llamada estrategia de rechazo).
Entre ellas, hay cuatro tipos de políticas de saturación:
1. DiscardOldestPolicy, descarta la tarea más antigua y agrega nuevas tareas a la cola.
2. AbortPolicy, se niega a ejecutar nuevas tareas y genera una excepción.
3.CallerRunsPolicy, el subproceso de la persona que llama ejecuta la nueva tarea, si el subproceso de la persona que llama está cerrado, la tarea se descarta.
4. DiscardPolicy, descarta directamente la nueva tarea.
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

usar

Por lo general, al usar el grupo de subprocesos, debe personalizarlo usted mismo. En términos generales, la teoría general es:
si se trata de una tarea que requiere un uso intensivo de la CPU: entonces el número de subprocesos centrales = el número de núcleos de CPU + 1; sumar 1 es para evitar que un subproceso no se pueda ejecutar debido a situaciones inesperadas, para maximizar la utilización de la CPU.
Si se trata de una tarea con uso intensivo de E/S: entonces la cantidad de subprocesos centrales = la cantidad de núcleos de CPU * 2.

De hecho, muchas tareas en el proceso de la aplicación no se pueden distinguir si son de uso intensivo de E/S o de uso intensivo de CPU. no es necesariamente el mejor Si busca el rendimiento Ultimate, debe explorar por sí mismo, o puede modificar dinámicamente la cantidad de subprocesos principales, el tiempo de inactividad y otros parámetros a través de algunos métodos establecidos del grupo de subprocesos.
Aquí hay un ejemplo simple:
package com.example.demo;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class MyThreadPool {
    private static final ThreadPoolExecutor executor;
    //线程所属的线程组
    private static final ThreadGroup group = new ThreadGroup("my-group");
    //线程名字的前缀
    private static final String prefix = "my-thread-";
    //线程计数器
    private static final AtomicInteger atomicInteger=new AtomicInteger(0);

    static {
        int core = Runtime.getRuntime().availableProcessors();
        ThreadFactory factory= r -> new Thread(group, r, prefix+atomicInteger.incrementAndGet());
        executor = new ThreadPoolExecutor(
                core,
                core*2,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(32),
                factory,
                //一共四种策略,这里采用调用者执行策略
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        //虚拟机退出时关闭线程池
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                executor.shutdown();
                try {
                    boolean flag;
                    do {
                        //一直等待线程池关闭完成
                        flag = executor.awaitTermination(1,TimeUnit.SECONDS);
                    } while (!flag);
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        
    }

    public static ThreadPoolExecutor getExecutor(){
        return executor;
    }
}
Probar grupo de subprocesos personalizado
package com.example.demo;

import org.junit.Test;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestClass {
    Random random = new Random();
    @Test
    public void test() throws InterruptedException {
        //采用闭锁等待所有线程池线程执行完成
        CountDownLatch latch=new CountDownLatch(50);
        ThreadPoolExecutor executor = MyThreadPool.getExecutor();
        for (int i=0;i<50;i++) {
            final int j = i;
            executor.execute(()->{
                try {
                    System.out.println(String.format("我是线程:%s,执行第%d个任务",
                            Thread.currentThread().getName(),j));
                    TimeUnit.SECONDS.sleep(random.nextInt(10));
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            });
        }
        latch.await();
    }
}

Principio de realización

estructura basica

Parte del código fuente es el siguiente, incluidos los atributos principales
public class ThreadPoolExecutor extends AbstractExecutorService {
    //线程池状态控制变量,一个int包含两部分,低29位为workCount,高3位为runState,初始为RUNNING状态,workCount=0
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //workCount最大2^29-1
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    //线程池运行的状态,共5种
    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;
    private static final int TERMINATED =  3 << COUNT_BITS;
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    //任务队列
    private final BlockingQueue<Runnable> workQueue;
    //访问变量用的互斥锁
    private final ReentrantLock mainLock = new ReentrantLock();
    //任务线程的集合
    private final HashSet<Worker> workers = new HashSet<Worker>();
    //awaitTermination()方法的条件变量
    private final Condition termination = mainLock.newCondition();

    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;
    }
}

método de ejecución

Se divide en 3 pasos:
1. Compare el número actual de subprocesos de trabajo con corePoolSize. Si es más pequeño que eso, intente iniciar un nuevo subproceso y use el comando de parámetro dado como su primera tarea. De lo contrario, realice 2 pasos. 2 Si la tarea se puede poner en cola con éxito
, luego verifique el estado de ejecución nuevamente, si no se está ejecutando, elimine la tarea de la cola, si no se puede poner en cola, realice el paso 3.
Si la tarea no se puede poner en cola, intente agregar un nuevo subproceso no central. Si el aumento falla, se aplica la política de denegación.
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    //判断线程个数是否小于核心线程数
    if (workerCountOf(c) < corePoolSize) {
        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);
        //启动一个线程,什么都不做
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //核心线程、任务队列都满了,增加非核心线程,如果也增加失败,则执行饱和策略
    else if (!addWorker(command, false))
        reject(command);
}

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        //获取运行状态
        int rs = runStateOf(c);

        // 运行状态>= SHUTDOWN,说明线程池进入关闭状态
        if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
            return false;

        for (;;) {
            //获取workCount
            int wc = workerCountOf(c);
            //线程超出个数,返回失败
            if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            //CAS操作,将workCount+1,成功则退出循环,否则重新读取控制变量
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            //CAS失败,如果状态不变继续进行内部for循环,如果变了则跳到外部for循环重新开始
            if (runStateOf(c) != rs)
                continue retry;
        }
    }
    //标识任务已启动
    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 {

                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                    //线程是活跃状态
                    if (t.isAlive())
                        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 {
        //线程没启动成功,当做增加失败处理,addWorkerFailed内部把workCount减1
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

método de envío

El método de envío se define en AbstractExecutorService :
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    //封装成RunnableFuture执行,RunnableFuture实现了Runnable
    RunnableFuture<T> ftask = newTaskFor(task);
    //执行
    execute(ftask);
    return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}
Llamable
Hay dos tipos de tareas que se ejecutan en el grupo de subprocesos, una no tiene parámetros ni valor de retorno, como el tipo Runnable, y la otra no tiene parámetros ni valor de retorno, es decir, el tipo Callable. La interfaz invocable se define de la siguiente manera:
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Obrero

Durante el proceso de envío de la tarea, se puede abrir un nuevo trabajador y la tarea en sí se asigna al trabajador como la primera tarea. Pero para un trabajador, en lugar de solo ejecutar una tarea, obtiene continuamente tareas de la cola para su ejecución, que es un proceso de bucle continuo.
private final class Worker extends AbstractQueuedSynchronizer 
implements Runnable {
    //.......
    /** 运行worker的线程. */
    final Thread thread;
    /** 接收的第一个任务 */
    Runnable firstTask;
    /** 执行完成的任务个数 */
    volatile long completedTasks;

    Worker(Runnable firstTask) {
        setState(-1); //初始为RUNNING状态
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    public void run() {
        runWorker(this);
    }
    //.......
}

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();//执行任务之前先上锁,AQS实现
            //检测线程池状态,如果状态>=STOP,则线程给自己发中断信号
            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;
                //完成的任务数+1
                w.completedTasks++;
                w.unlock();
            }
        }
        //可根据此变量判断Worker以何种方式退出(正常,中断,其他异常)
        completedAbruptly = false;
    } finally {
        //清理worker
        processWorkerExit(w, completedAbruptly);
    }
}

método de gancho

ThreadPoolExecutor proporciona los siguientes métodos de enlace. Si desea heredar ThreadPoolExecutor para implementar su propio grupo de subprocesos, puede considerar implementar los siguientes métodos.
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }

Cerrar el grupo de hilos

Al cerrar un grupo de subprocesos, algunos subprocesos todavía están ejecutando una determinada tarea, algunas personas que llaman envían tareas al grupo de subprocesos y puede haber tareas sin ejecutar en la cola. Por lo tanto, el proceso de apagado no puede ser instantáneo, sino que requiere una transición sin problemas, lo que implica la administración completa del ciclo de vida del grupo de subprocesos.
proceso de transición estatal
El grupo de subprocesos tiene dos funciones de apagado, shutdown() y shutdownNow(), que cambiarán el grupo de subprocesos a diferentes estados. Pero no importa en qué estado se encuentre, después de que la cola esté vacía y el grupo de subprocesos también esté vacío, cambiará al estado ORDENANDO; finalmente, ejecute un método de enganche terminado (), ingrese al estado TERMINADO y el grupo de subprocesos será verdaderamente cerrado.
La migración estatal es de pequeño a grande, en el orden de -1, 0, 1, 2, 3, solo migración hacia adelante, no hacia atrás.
La diferencia entre los dos cierres.
El método shutdown() no borrará la cola de tareas, esperará a que se ejecuten todas las tareas y shutdownNow() borrará la cola de tareas.
shutdown() solo interrumpirá los subprocesos inactivos, y shutdownNow() interrumpirá todos los subprocesos.

tryTerminate() no terminará a la fuerza el grupo de subprocesos, sino que solo realizará alguna detección: cuando el número de trabajadores es 0 y la cola de trabajadores está vacía, primero cambie el estado a ORDENAR y luego llame a la función de gancho terminada(). Cuando se complete la ejecución de la función de gancho, cambie el estado de ORDENANDO a TERMINADO y luego llame a terminación.sinaglAll() para notificar a todos los subprocesos de llamadas previamente bloqueados en awaitTermination.

Parte del código fuente es el siguiente:
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //检查是否有关闭线程池的权限
        checkShutdownAccess();
        //CAS操作,将线程池状态迁移为SHUTDOWN
        advanceRunState(SHUTDOWN);
        //关闭空闲的线程
        interruptIdleWorkers();
        //给ScheduledThreadPoolExecutor留的钩子方法
        onShutdown();
    } finally {
        mainLock.unlock();
    }
    //迁移到TERMINATED状态
    tryTerminate();
}
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //检查是否有关闭线程池的权限
        checkShutdownAccess();
        //CAS操作,将线程池状态迁移为STOP
        advanceRunState(STOP);
        //关闭所有线程
        interruptWorkers();
        //移除任务队列的元素并返回它们
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    //迁移到TERMINATED状态
    tryTerminate();
    return tasks;
}

private void interruptIdleWorkers() {
    interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            //线程未中断且Worker尝试加锁成功,说明线程处于空闲状态,如果加锁失败,说明线程当前持有锁,正在执行任务
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}
private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //中断所有线程
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        if (workerCountOf(c) != 0) {
            interruptIdleWorkers(ONLY_ONE);
            return;
        }
        //任务队列为空,workCount=0会走到这里
        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));
                    //通知等待的awaitTermination()方法
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // 迁移状态失败则重新循环
    }
}
Método de cierre recomendado
executor.shutdown();
try {
    boolean flag;
    do {
        //一直等待线程池关闭完成
        flag = executor.awaitTermination(1,TimeUnit.SECONDS);
    } while (!flag);
} catch (Exception e){
    e.printStackTrace();
}

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor hereda de ThreadPoolExecutor.Además de las funciones básicas del grupo de subprocesos, también implementa
ScheduledExecutorService, que puede retrasar la ejecución de tareas o programar tareas según la frecuencia, lo que se materializa en los siguientes dos aspectos:
1. Ejecución de tareas retrasada
2. Ejecución de tareas periódicas Los métodos
correspondientes son los siguientes:
//延迟执行任务
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit);
public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit);

//周期执行任务,按固定频率执行,与任务本身执行时间无关,但是任务执行时间必须小于间隔时间,例如间隔时间是5s,每5s执行一次任务,任务的执行时间必须小于5s。
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,
TimeUnit unit);
//周期执行任务,按固定间隔执行,与任务本身执行时间有关。假如任务本身执行时间是10s,间隔2s,则下一次开始执行的时间就是12s。
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,
long delay,TimeUnit unit);

usar

La construcción del grupo de subprocesos de programación es relativamente simple. El método de construcción sobrecargado requiere como máximo 3 parámetros, que son el número de subprocesos principales , la fábrica de subprocesos y la estrategia de saturación. A continuación, se muestran brevemente varias formas de retrasar la programación:
public class TestClass {
    private static final ThreadGroup group = new ThreadGroup("schedule-group");
    private static final String prefix = "schedule-thread-";
    private static final AtomicInteger atomicInteger=new AtomicInteger(0);
    private static final ThreadLocal<DateTimeFormatter> timeFormatters=new ThreadLocal();

    private DateTimeFormatter getFormatter(){
        DateTimeFormatter formatter = timeFormatters.get();
        if (Objects.isNull(formatter)){
            DateTimeFormatter formatter1=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
            timeFormatters.set(formatter1);
            return formatter1;
        }
        return formatter;
    }
    @Test
    public void schedule(){
        ThreadFactory factory= r -> new Thread(group, r ,prefix+atomicInteger.incrementAndGet());
        int i = Runtime.getRuntime().availableProcessors();
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(i, factory, new ThreadPoolExecutor.CallerRunsPolicy());
        System.out.println("启动调度线程池:"+getFormatter().format(LocalDateTime.now()));
        //延迟执行,执行一次
        ScheduledFuture<String> delay = executor.schedule(
                () -> "单次延迟执行开始时间:"+getFormatter().format(LocalDateTime.now()),
                3, TimeUnit.SECONDS);
        //固定频率执行
        executor.scheduleAtFixedRate(() ->
                System.out.println("固定频率开始执行:"+getFormatter().format(LocalDateTime.now())),
                1,3, TimeUnit.SECONDS);
        //固定延迟执行
        executor.scheduleWithFixedDelay(()->
                System.out.println("固定延迟开始执行:"+getFormatter().format(LocalDateTime.now())),
                        1,3,TimeUnit.SECONDS);
        while (!delay.isDone()) {
            try {
                String s = delay.get();
                System.out.println("单次延迟执行结果:"+s);
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
            break;
        }
        while (true){}
    }
}

Principio de realización

El método de construcción llama al método de construcción de ThreadPoolExecutor , la cantidad máxima de subprocesos se establece en el valor máximo de int, el tiempo de inactividad también se establece en 0 y la cola de tareas también se fija como una cola de demora personalizada.
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }

estructura basica

Cola de trabajo retrasado
La cola de tareas de ScheduledThreadPoolExecutor utiliza una cola de retraso personalizada DelayedWorkQueue ,
que no es muy diferente de las colas de retraso normales. Esta clase interna estática se define de la siguiente manera:
static class DelayedWorkQueue extends AbstractQueue<Runnable>
        implements BlockingQueue<Runnable> {

        private static final int INITIAL_CAPACITY = 16;
        //RunnableScheduledFuture的唯一实现是ScheduledFutureTask
        private RunnableScheduledFuture<?>[] queue =
            new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
        private final ReentrantLock lock = new ReentrantLock();
        private int size = 0;
        //队头等待的线程
        private Thread leader = null;
        //当队列头部有新的任务可用或新线程可能需要成为leader时发出的条件
        private final Condition available = lock.newCondition();
        //......
}
Tarea futura programada
ScheduledThreadPoolExecutor también tiene una clase interna ScheduledFutureTask , que se implementa de la siguiente manera:
private class ScheduledFutureTask<V>
        extends FutureTask<V> implements RunnableScheduledFuture<V> {
    //任务序号
    private final long sequenceNumber;
    //以纳秒为单位,启动任务的时间(周期任务启动时间一直会累加)
    private long time;
    //周期(以纳秒为单位)用于重复任务。正值表示固定速率执行。负值表示固定延迟执行。0表示非重复任务
    private final long period;
    //通过reExecutePeriodic()方法重新加入队列的实际任务
    RunnableScheduledFuture<V> outerTask = this;
    //当前任务在延迟队列中的索引,用于更快的取消任务
    int heapIndex;

    ScheduledFutureTask(Runnable r, V result, long ns) {
        super(r, result);
        this.time = ns;
        this.period = 0;
        this.sequenceNumber = sequencer.getAndIncrement();
    }

    ScheduledFutureTask(Runnable r, V result, long ns, long period) {
        super(r, result);
        this.time = ns;
        this.period = period;
        this.sequenceNumber = sequencer.getAndIncrement();
    }

    ScheduledFutureTask(Callable<V> callable, long ns) {
        super(callable);
        this.time = ns;
        this.period = 0;
        this.sequenceNumber = sequencer.getAndIncrement();
    }

    public long getDelay(TimeUnit unit) {
        return unit.convert(time - now(), NANOSECONDS);
    }

    public int compareTo(Delayed other) {
        if (other == this) 
            return 0;
        if (other instanceof ScheduledFutureTask) {
            ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
            long diff = time - x.time;
            if (diff < 0)
                return -1;
            else if (diff > 0)
                return 1;
            else if (sequenceNumber < x.sequenceNumber)
                return -1;
            else
                return 1;
        }
        long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
        return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
    }

    public boolean isPeriodic() {
        return period != 0;
    }

    private void setNextRunTime() {
        long p = period;
        if (p > 0)
            time += p;
        else
            time = triggerTime(-p);
    }

    public boolean cancel(boolean mayInterruptIfRunning) {
        boolean cancelled = super.cancel(mayInterruptIfRunning);
        if (cancelled && removeOnCancel && heapIndex >= 0)
            remove(this);
        return cancelled;
    }

    public void run() {
        boolean periodic = isPeriodic();
        if (!canRunInCurrentRunState(periodic))
            cancel(false);
        else if (!periodic)
            ScheduledFutureTask.super.run();
        else if (ScheduledFutureTask.super.runAndReset()) {
            setNextRunTime();//设置下一次启动的时间
            reExecutePeriodic(outerTask);//重新入队
        }
    }
}

método de programación (ejecutable)

public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit) {
    if (command == null || unit == null)
        throw new NullPointerException();
    //把Runnable包装成RunnableScheduledFuture,ScheduledFutureTask是其实现
    RunnableScheduledFuture<?> t = decorateTask(command,
        new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit)));
    delayedExecute(t);
    return t;
}

protected <V> RunnableScheduledFuture<V> decorateTask(
    Runnable runnable, RunnableScheduledFuture<V> task) {
    return task;
}

private void delayedExecute(RunnableScheduledFuture<?> task) {
    //线程池关闭则执行拒绝策略
    if (isShutdown())
        reject(task);
    else {
        //把任务放入延迟队列
        super.getQueue().add(task);
        //线程池关闭或者 当前线程池状态不能运行任务,且从队列中移除任务成功,则取消任务
        if (isShutdown() &&
            !canRunInCurrentRunState(task.isPeriodic()) &&
            remove(task))
            task.cancel(false);
        else
            ensurePrestart();
    }
}

void ensurePrestart() {
    int wc = workerCountOf(ctl.get());
    //当小于核心线程数只开启新线程(不运行任务)
    if (wc < corePoolSize)
        addWorker(null, true);
    //即使corePoolSize=0,当第一次运行时,也启动一个新线程
    else if (wc == 0)
        addWorker(null, false);
}

métodos scheduleWithFixedDelay y scheduleAtFixedRate

La implementación de los dos métodos es casi exactamente la misma, la única diferencia es que uno de los métodos de construcción de ScheduledFutureTask es un número positivo y el otro es un número negativo.En ScheduledFutureTask, un valor positivo indica una tasa fija de ejecución . Los valores negativos indican una ejecución de latencia fija. 0 significa tareas que no se repiten.
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                 long initialDelay,
                                                 long delay,
                                                 TimeUnit unit) {
    if (command == null || unit == null)
        throw new NullPointerException();
    if (delay <= 0)
        throw new IllegalArgumentException();
    ScheduledFutureTask<Void> sft =
        new ScheduledFutureTask<Void>(command,
                                      null,
                                      triggerTime(initialDelay, unit),
                                      unit.toNanos(-delay));//负数
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    delayedExecute(t);
    return t;
}

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit) {
    if (command == null || unit == null)
        throw new NullPointerException();
    if (period <= 0)
        throw new IllegalArgumentException();
    ScheduledFutureTask<Void> sft =
        new ScheduledFutureTask<Void>(command,
                                      null,
                                      triggerTime(initialDelay, unit),
                                      unit.toNanos(period));//正数
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    delayedExecute(t);
    return t;
}

private long triggerTime(long delay, TimeUnit unit) {
    return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
}
long triggerTime(long delay) {
    return now() +
        ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}

Futuro

La relación de herencia de Future se muestra en la siguiente figura.

RunnableFuturo

La interfaz se define de la siguiente manera, heredando las interfaces Runnable y Future:
public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    void run();
}

FuturoTarea

Esta clase es la implementación de RunnableFuture, y hay un Callable dentro, que actúa como un adaptador para convertir Callable en Runnable para su ejecución.

estructura basica

public class FutureTask<V> implements RunnableFuture<V> {
    //任务状态
    private volatile int state;
    //初始状态
    private static final int NEW          = 0;
    //中间状态
    private static final int COMPLETING   = 1;
    //以下都是最终状态
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    //中间状态
    private static final int INTERRUPTING = 5;
    //最终状态
    private static final int INTERRUPTED  = 6;

    /** 实际要执行的Callable,通过构造函数传入 */
    private Callable<V> callable;
    /** 存放执行结果或者抛出的异常 */
    private Object outcome;
    /** 运行Callable的线程 */
    private volatile Thread runner;
    /** 在get方法等待的线程链表 */
    private volatile WaitNode waiters;

    @SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       
    }

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       
    }

    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
}

método de ejecución

public void run() {
    //任务状态不是NEW 或者 其他线程在执行任务,结束方法
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                //执行
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                //异常存入outcome变量
                setException(ex);
            }
            if (ran)
                //返回值存入outcome变量
                set(result);
        }
    } finally {
        //置空
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)//处理中断
            handlePossibleCancellationInterrupt(s);
    }
}
//状态 NEW->COMPLETING->EXCEPTIONAL
protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        //通知等待的线程
        finishCompletion();
    }
}

protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        //通知等待的线程
        finishCompletion();
    }
}

private void handlePossibleCancellationInterrupt(int s) {
    // It is possible for our interrupter to stall before getting a
    // chance to interrupt us.  Let's spin-wait patiently.
    if (s == INTERRUPTING)
        while (state == INTERRUPTING)
            Thread.yield(); // wait out pending interrupt

    // assert state == INTERRUPTED;

    // We want to clear any interrupt we may have received from
    // cancel(true).  However, it is permissible to use interrupts
    // as an independent mechanism for a task to communicate with
    // its caller, and there is no way to clear only the
    // cancellation interrupt.
    //
    // Thread.interrupted();
}

private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) {
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                Thread t = q.thread;
                if (t != null) {
                    q.thread = null;
                    //唤醒
                    LockSupport.unpark(t);
                }
                WaitNode next = q.next;
                if (next == null)
                    break;
                q.next = null; 
                q = next;
            }
            break;
        }
    }
    //空实现
    done();
    callable = null;
}

obtener método

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)//不是正在完成状态,无限期等待
        s = awaitDone(false, 0L);
    return report(s);
}

public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)//等待指定时间
        throw new TimeoutException();
    return report(s);
}
//返回结果
private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
        return (V)x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        if (Thread.interrupted()) {//线程中断了则从链表中移除
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        else if (s == COMPLETING) // 正处于中间状态
            Thread.yield();
        else if (q == null)
            q = new WaitNode();
        else if (!queued)//入队
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else//阻塞
            LockSupport.park(this);
    }
}

Ejecutores

这个类是Executor框架的辅助工具类,用来创建预定义的线程池和一些任务(Callable)。在实际环境下,不建议直接使用此工具创建的线程池,特别是无界阻塞队列和不限制线程个数的实现,容易造成OOM,资源耗尽等严重问题。
//每来一个任务,就创建一个线程
ExecutorService executorService1 = Executors.newCachedThreadPool();
//固定线程数的线程池
ExecutorService executorService2 = Executors.newFixedThreadPool(6);
//多线程的调度线程池
ExecutorService executorService3 = Executors.newScheduledThreadPool(8);
//单线程线程池
ExecutorService executorService4 = Executors.newSingleThreadExecutor();
//单线程调度线程池
ExecutorService executorService5 = Executors.newSingleThreadScheduledExecutor();
//forkjoin线程池
ExecutorService executorService6 = Executors.newWorkStealingPool();

Supongo que te gusta

Origin blog.csdn.net/qq_32076957/article/details/128638425
Recomendado
Clasificación