Un artículo para comprender el uso y el mecanismo operativo del grupo de subprocesos de Java ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor hereda de ThreadPoolExecutor. Se utiliza principalmente para ejecutar tareas después de un retraso determinado o para realizar tareas de forma regular. La función de ScheduledThreadPoolExecutor es similar a Timer, pero la función de ScheduledThreadPoolExecutor es más potente y flexible. Timer corresponde a un único subproceso en segundo plano, y ScheduledThreadPoolExecutor puede especificar varios números correspondientes de subprocesos en segundo plano en el constructor.

Similar a ThreadPoolExecutor, ScheduledThreadPoolExecutor también admite dos formas de uso. La primera es crear una instancia a través del método de construcción y la segunda es crearla a través de Executors. Las siguientes son dos formas de crear un ScheduledThreadPoolExecutor:

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);

ScheduledThreadPoolExecutor proporciona cuatro métodos para ejecutar tareas y programar tareas después de un tiempo de retraso determinado. A continuación, se muestra un análisis de los cuatro métodos proporcionados por ScheduledThreadPoolExecutor:

//command为要执行的任务,delay指定在delay时间之后执行,unit为时间单位
public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit){......}
//callable为要执行的任务,delay指定在delay时间之后执行,unit为时间单位
//区别是一个有返回值,一个没返回值
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit){......}
//command为要执行的任务,以period为固定周期时间,按照一定频率来重复执行任务,initialDelay是说系统启动后,等待initialDelay时间开始执行
 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {
}
//这个是以delay为固定延迟时间,按照一定的等待时间来执行任务,initialDelay意义与上面的相同。
//该方法与上面的类似,但是含义又不同,上面的方法是以固定的频率执行,而该方法是在任务执行之后在delay时间后再次执行。
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit){}

A continuación usamos el método scheduleWithFixedDelay como ejemplo para explicar el uso de ScheduledThreadPoolExecutor, de la siguiente manera escribimos un hilo Runnable, que tiene una variable i aumento y duerme i segundos. el código se muestra a continuación:

AtomicInteger i = new AtomicInteger();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
executor.scheduleWithFixedDelay(()->{
    System.out.println("执行定时任务");
},2, 2, TimeUnit.SECONDS);

Cuando instanciamos ScheduledThreadPoolExecutor, podemos ver el método de construcción de ScheduledThreadPoolExecutor, que construye internamente un DelayQueue. El código es el siguiente:

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}

DelayQueue es una cola ilimitada. Cuando
se llama al método scheduleAtFixedRate () o scheduleWith- FixedDelay () de ScheduledThreadPoolExecutor, se agrega una ScheduledFutureTask que implementa la interfaz RunnableScheduledFutur a la DelayQueue de ScheduledThreadPoolExecutor. Los subprocesos del grupo de subprocesos obtienen ScheduledFutureTask de DelayQueue y luego ejecutan la tarea.

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {
        ,.....
    、//将要执行的任务command封装为ScheduledFutureTask对象
    ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));
    //将ScheduledFutureTask和command装饰为RunnableScheduledFuture
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    //该方法会将任务放到DelayQueue中,代码如下:
    delayedExecute(t);
    return t;
}
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();
    }
}

ScheduledThreadPoolExecutor colocará la tarea a programar (ScheduledFutureTask) en un DelayQueue. ScheduledFutureTask contiene principalmente tres variables miembro: time, que representa la hora específica en que se ejecutará esta tarea; sequenceNumber, que representa el número de secuencia de esta tarea que se agregará a ScheduledThreadPoolExecutor; período, que representa el intervalo entre la ejecución de la tarea. DelayQueue encapsula PriorityQueue, que ordena ScheduledFutureTask en la cola. Al ordenar, el tiempo más pequeño se clasifica primero (la tarea anterior se ejecutará primero). Si el tiempo de dos ScheduledFutureTasks es el mismo, compare el número de secuencia y el número de secuencia más pequeño se clasifica en primer lugar (es decir, si el tiempo de ejecución de las dos tareas es el mismo, la tarea enviada primero se ejecutará primero).

Después de presentar cómo ScheduledThreadPoolExecutor coloca tareas en la cola, veamos cómo ScheduledThreadPoolExecutor saca tareas de la cola y las ejecuta. Es decir, el ciclo de ejecución de ScheduledThreadPoolExecutor. En términos generales, se requieren cuatro pasos como se muestra en la siguiente figura:

En los pasos anteriores, primero obtenga la ScheduledFutureTask caducada (DelayQueue.take ()) de DelayQueue. Luego ejecute esta ScheduledFutureTask. Y modifique la variable de tiempo de ScheduledFutureTask a la hora que se ejecutará la próxima vez. Finalmente, vuelva a colocar ScheduledFutureTask en DelayQueue. Primero, veamos cómo eliminar tareas de la cola:

public RunnableScheduledFuture<?> take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (;;) {
            //获取第一个队列中的任务
            RunnableScheduledFuture<?> first = queue[0];
            //如果PriorityQueue为空,当前线程到Condition中等待
            if (first == null)
                available.await();
            else {
                long delay = first.getDelay(NANOSECONDS);
                //如果時間<=0,實行任務
                if (delay <= 0)
                    return finishPoll(first);
                first = null; 
                if (leader != null)
                    available.await();
                else {
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {   
                        //如果PriorityQueue的头元素的time时间比当前时间大,到Condition中等待到time时间
                        available.awaitNanos(delay);
                    } finally {
                    if (leader == thisThread)
                          leader = null;
                      }
                }
             }
         }
    } finally {
         if (leader == null && queue[0] != null)
              available.signal();
    lock.unlock();
    }
}

Finalmente, analizamos el paso final 4 del hilo en ScheduledThreadPoolExecutor para ejecutar la tarea, el proceso de poner ScheduledFutureTask en DelayQueue. A continuación se muestra la implementación del código fuente de DelayQueue.add ().

 public boolean offer(Runnable x) {
    if (x == null)
        throw new NullPointerException();
    RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
    //加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        int i = size;
        //队列扩容
        if (i >= queue.length)
            grow();
            size = i + 1;
            if (i == 0) {
            queue[0] = e;
            //加入队列
            setIndex(e, 0);
        } else {
            //加入队列
            siftUp(i, e);
        }
        if (queue[0] == e) {
            leader = null;
                    available.signal();
         }
      } finally {
    lock.unlock();
    }
    return true;
}

 

Supongo que te gusta

Origin blog.csdn.net/wk19920726/article/details/108623388
Recomendado
Clasificación