手撕源码系列之线程池 -- ScheduledThreadPoolExecutor(二)

ScheduledThreadPoolExecutor的具体实现


在这里插入图片描述

任务添加

ScheduledThreadPoolExecutor对任务的添加提供了大量的方法。

  1. 重写ThreadPoolExecutor
    // 执行任务command
    public void execute(Runnable command) {
          
          
        schedule(command, 0, TimeUnit.NANOSECONDS);
    }
    
  2. 重写AbstractExecutorService
    // 执行任务task
    public Future<?> submit(Runnable task) {
          
          
        return schedule(task, 0, TimeUnit.NANOSECONDS);
    }
    
    // 执行任务task
    public <T> Future<T> submit(Runnable task, T result) {
          
          
        return schedule(Executors.callable(task, result), 0, TimeUnit.NANOSECONDS);
    }
    
    // 执行任务task
    public <T> Future<T> submit(Callable<T> task) {
          
          
        return schedule(task, 0, TimeUnit.NANOSECONDS);
    }
    
  3. 实现ScheduledExecutorService接口
    // 延迟执行任务command,延迟时间由delay和unit决定,只执行一次
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
          
          
        // 检查参数
        if (command == null || unit == null)
            throw new NullPointerException();
        /**
         * 使用triggerTime()计算执行时间,创建一个RunnableScheduledFuture对象
         * 默认的decorateTask()方法什么都没做,直接将传入的ScheduledFutureTask返回了
         */
        RunnableScheduledFuture<?> t = decorateTask(command,
                new ScheduledFutureTask<Void>(command, null,
                        triggerTime(delay, unit)));
        // 延迟执行任务
        delayedExecute(t);
        return t;
    }
    
    // 延迟执行会产生执行结果任务command,延迟时间由delay和unit决定;只执行一次
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
          
          
        // 检查参数
        if (callable == null || unit == null)
            throw new NullPointerException();
        /**
         * 使用triggerTime()计算执行时间,创建一个RunnableScheduledFuture对象
         * 默认的decorateTask()方法什么都没做,直接将传入的ScheduledFutureTask返回了
         */
        RunnableScheduledFuture<V> t = decorateTask(callable,
                new ScheduledFutureTask<V>(callable,
                        triggerTime(delay, unit)));
        // 延迟执行任务
        delayedExecute(t);
        return t;
    }
    
    /**
     * 该方法在initialDelay时长后第一次执行任务,以后每隔period时长,再次执行任务。
     * 注意,period是从任务开始执行算起的。
     * 开始执行任务后,定时器每隔period时长检查该任务是否完成,
     * 如果完成则再次启动任务,否则等该任务结束后才再次启动任务
     */
    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();
        // 使用triggerTime()计算执行时间,创建一个ScheduledFutureTask对象
        ScheduledFutureTask<Void> sft =
                new ScheduledFutureTask<Void>(command, null,
                        triggerTime(initialDelay, unit), unit.toNanos(period));
        // 使用decorateTask()对ScheduledFutureTask对象进行装饰
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        // 执行任务
        delayedExecute(t);
        return t;
    }
    
    /**
     * 该方法在initialDelay时长后第一次执行任务,
     * 以后每当任务执行完成后,等待delay时长,再次执行任务
     */
    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();
        // 使用triggerTime()计算执行时间,创建一个ScheduledFutureTask对象
        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;
    }
    

任务执行

当ScheduledThreadPoolExecutor将添加的任务都放入DelayedWorkQueue队列中后,如果线程池符合运行状态,会调用ensurePrestart()预启动Worker线程,这个方法位于父类ThreadPoolExecutor中,我们回顾一下:

void ensurePrestart() {
    
    
    // 获取workerCount
    int wc = workerCountOf(ctl.get());
    // 如果workerCount小于corePoolSize
    if (wc < corePoolSize)
        // 添加核心线程
        addWorker(null, true);
    else if (wc == 0)
        // 添加非核心线程
        addWorker(null, false);
}
  1. 添加Worker线程后,它最终会启动Worker自线程,即调用Worker的run()方法,而run()方法则会调用ThreadPoolExecutor的runWorker(Worker w)方法并将Worker自己传入。
  2. 该方法的while循环条件中会不断取出Worker的firstTask,如果firstTask为null就会调用getTask()方法获取Runnable任务对象并调用其run()方法以实现任务的运行。在ScheduledThreadPoolExecutor中,由于所有Worker都是预创建的,firstTask都为null,因此会调用getTask()方法获取任务;
  3. 而getTask()方法则实现了从等待队列中获取任务:
private Runnable getTask() {
    
    
    // 用于记录poll()方法是否超时
    boolean timedOut = false; // Did the last poll() time out?

    retry:
    // 无限循环
    for (; ; ) {
    
    
        // 获取ctl和runState
        int c = ctl.get();
        int rs = runStateOf(c);

        // 检查状态
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            /**
             * 如果runState为STOP、TIDYING或TERMINATED
             * 或者runState为SHUTDOWN,且workerQueue为空
             * 将workerCount减1,返回null结束运行
             */
            decrementWorkerCount();
            return null;
        }

        // 记录worker是否能够被移除
        boolean timed;      // Are workers subject to culling?

        // 无限循环
        for (; ; ) {
    
    
            // 获取workerCount
            int wc = workerCountOf(c);
            /**
             * 判断当前Worker是否可以被移除,即当前Worker是否可以一直等待任务。
             * 如果allowCoreThreadTimeOut为true,或者workerCount大于核心线程数,
             * 则当前线程是有超时时间的(keepAliveTime),无法一直等待任务
             */
            timed = allowCoreThreadTimeOut || wc > corePoolSize;
            /**
             * workerCount小于等于核心线程数,且没有超时,则跳出内层循环
             */
            if (wc <= maximumPoolSize && !(timedOut && timed))
                break;
            // 否则表示已超时,将workerCount减1,如果成功直接返回null
            if (compareAndDecrementWorkerCount(c))
                return null;
            // 走到这里说明上一步workerCount减1失败了,重新读取ctl
            c = ctl.get();  // Re-read ctl
            // 如果与之前的runState不同,表示线程池状态发生改变了,跳出到外层循环重试
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }

        try {
    
    
            /**
             * 根据线程是否会超时来分别调用相应的方法,
             * poll()方法附带超时机制;take()方法没有超时机制,并且可能会阻塞等待
             */
            Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
            // 如果获取到的任务不为null则返回
            if (r != null)
                return r;
            // 走到这里表示获取操作超时了
            timedOut = true;
        } catch (InterruptedException retry) {
    
    
            // 被中断,可能是超时等待过程中被中断了
            timedOut = false;
        }
    }
}
  1. 因为ScheduledThreadPoolExecutor种keepAliveTime默认是0,所以getTask方法种实际调用的是take()方法,而任务的延时执行,正是由DelayedWorkQueue的出队操作take()实现的,它会根据任务的延迟时间决定是否可以出队,从而达到延迟执行任务。
  2. 任务真实执行是调用的任务ScheduledFutureTask的run()方法
/**
 * Overrides FutureTask version so as to reset/requeue if periodic.
 * 任务的具体执行
 */
public void run() {
    
    
    // 任务是否是周期性的
    boolean periodic = isPeriodic();
    // 判断当前周期性任务是否可以执行
    if (!canRunInCurrentRunState(periodic))
        // 如果不能执行,就取消当前任务
        cancel(false);
    else if (!periodic)
        // 如果不是周期性任务,直接执行即可
        ScheduledFutureTask.super.run();
    else if (ScheduledFutureTask.super.runAndReset()) {
    
    
        // 否则执行任务并重置,然后计算并设置下次执行时间
        setNextRunTime();
        // 重新添加下一次任务,即将当前ScheduledFutureTask重新入队
        reExecutePeriodic(outerTask);
    }
}

重复性任务的执行

根据上面的讲解ScheduledFutureTask中run()方法的内容,当任务执行完成并线程池状态重置成功后,会调用 setNextRunTime() 计算并设置任务的下一次执行执行时间,然后调用reExecutePeriodic(outerTask)重新添加下一次任务,该方法源码如下:

/**
 * Requeues a periodic task unless current run state precludes it.
 * Same idea as delayedExecute except drops task rather than rejecting.
 *
 * 周期性任务的重新入队
 *
 * @param task the task
 */
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
    
    
    // 判断当前线程池状态是否可以运行周期性任务
    if (canRunInCurrentRunState(true)) {
    
    
        // 任务重新入队
        super.getQueue().add(task);
        /**
         * 重新检查当前线程池状态是否可以运行周期性任务,如果不行就从队列中移除任务
         * 并取消任务的执行
         */
        if (!canRunInCurrentRunState(true) && remove(task))
            task.cancel(false);
        else
            // 如果可以运行就预启动Worker线程
            ensurePrestart();
    }
}

任务的取消

代码比较简单,调用了父类FutureTask的cancel(boolean mayInterruptIfRunning)方法,内部会根据传入参数决定是否对任务线程进行中断。在任务成功取消后,会根据removeOnCancel和heapIndex决定是否将任务从队列中移除。这里讲解一下removeOnCancel成员变量,它属于ScheduledThreadPoolExecutor类,用于控制任务在取消时是否从队列移除,默认为false;

// 取消任务的执行
public boolean cancel(boolean mayInterruptIfRunning) {
    
    
    // 调用父类方法取消任务的执行
    boolean cancelled = super.cancel(mayInterruptIfRunning);
    // 如果取消成功,且removeOnCancel策略为true,任务的heapIndex大于等于0
    if (cancelled && removeOnCancel && heapIndex >= 0)
        // 则将任务从队列中移除
        remove(this);
    // 返回是否取消成功的结果
    return cancelled;
}

猜你喜欢

转载自blog.csdn.net/pontuss/article/details/114636407