java-ScheduledThreadPoolExecutor源码分析

115 阅读4分钟

入口

调度任务的入口:java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate

/**
     * 调度执行
     *
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     * @throws IllegalArgumentException   {@inheritDoc}
     */
    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;
    }

这个方法只是入口,没有干具体的事情。

继续看delayedExecute方法。

添加业务线程到队列

delayedExecute方法的主要步骤是:

1.先把业务线程添加到队列

2.创建调度线程(即Worker线程)

java.util.concurrent.ScheduledThreadPoolExecutor#delayedExecute

/**
     * 主要步骤
     * 1.先把业务线程添加到队列
     * 2.创建调度线程(即Worker线程)
     * 
     * ---
     * Main execution method for delayed or periodic tasks.  If pool
     * is shut down, rejects the task. Otherwise adds task to queue
     * and starts a thread, if necessary, to run it.  (We cannot
     * prestart the thread to run the task because the task (probably)
     * shouldn't be run yet.)  If the pool is shut down while the task
     * is being added, cancel and remove it if required by state and
     * run-after-shutdown parameters.
     *
     * @param task the 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
                //创建调度线程(即Worker线程)
                ensurePrestart();
        }
    }

以上代码,都是ScheduledThreadPoolExecutor类里的代码。

可以看到,ScheduledThreadPoolExecutor只是调用了ensurePrestart方法去创建调度线程,而从ensurePrestart方法开始,已经进入到了ThreadPoolExecutor类里面。

创建调度线程,添加调度线程到线程池,启动调度线程

ensurePrestart方法只是调用创建调度线程的方法addWorker:java.util.concurrent.ThreadPoolExecutor#ensurePrestart

/**
     * 创建调度线程
     * 
     * Same as prestartCoreThread except arranges that at least one
     * thread is started even if corePoolSize is 0.
     */
    void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        if (wc < corePoolSize)
            //创建调度线程
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }

addWorker方法是线程池ThreadPoolExecutor类的方法,这个是各种不同线程池的公共方法。

为什么ScheduledThreadPoolExecutor会调用ThreadPoolExecutor里的方法?

因为ScheduledThreadPoolExecutor继承了ThreadPoolExecutor。

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {

java.util.concurrent.ThreadPoolExecutor#addWorker,主要步骤如下:

1.创建新的调度线程(即Worker线程)

2.并且添加新的调度线程到线程池

3.最后启动调度线程

/**
     * 主要步骤
     * 1.创建新的调度线程(即Worker线程)
     * 2.并且添加新的调度线程到线程池
     * 3.最后启动调度线程
     *
     * ---
     * Checks if a new worker can be added with respect to current
     * pool state and the given bound (either core or maximum). If so,
     * the worker count is adjusted accordingly, and, if possible, a
     * new worker is created and started, running firstTask as its
     * first task. This method returns false if the pool is stopped or
     * eligible to shut down. It also returns false if the thread
     * factory fails to create a thread when asked.  If the thread
     * creation fails, either due to the thread factory returning
     * null, or due to an exception (typically OutOfMemoryError in
     * Thread.start()), we roll back cleanly.
     *
     * @param firstTask the task the new thread should run first (or
     * null if none). Workers are created with an initial first task
     * (in method execute()) to bypass queuing when there are fewer
     * than corePoolSize threads (in which case we always start one),
     * or when the queue is full (in which case we must bypass queue).
     * Initially idle threads are usually created via
     * prestartCoreThread or to replace other dying workers.
     *
     * @param core if true use corePoolSize as bound, else
     * maximumPoolSize. (A boolean indicator is used here rather than a
     * value to ensure reads of fresh values after checking other pool
     * state).
     * @return true if successful
     */
    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 &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;


            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //线程池线程数量加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);
            final Thread t = w.thread;
            if (t != null) {
                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());


                    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) {
                    //执行Worker线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

执行业务线程

执行业务线程的代码,其实也是使用ThreadPoolExecutor的公共代码:java.util.concurrent.ThreadPoolExecutor#runWorker。

本质其实就是让调度线程,去不停的执行业务线程。

之前的文章讲过,这里不细讲。可以参考:juejin.cn/post/712319…

调度线程池和一般线程池的唯一区别是,多了一个定时任务的功能,即会重复执行同一个任务。

那源码是如何实现的呢?主要步骤如下:

  1. 执行业务线程
  2. 因为业务线程被封装了,所以先执行的是封装业务线程
  3. 最后,才执行真正的业务线程

执行业务线程

执行业务线程,是在哪里触发?

前面说了,是在java.util.concurrent.ThreadPoolExecutor#runWorker方法触发。

因为业务线程被封装了,所以先执行的是封装业务线程

刚才的执行业务线程,并不是真正的业务线程,而是被封装的业务线程。

为什么要封装?就是多了一个定时执行任务的功能,本质其实就是重复执行任务。

被封装的业务线程,代码如下:java.util.concurrent.ScheduledThreadPoolExecutor.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(); //设置下一次执行时间
                //把同一个业务线程再次添加到队列:目的是为了重复执行
                reExecutePeriodic(outerTask);
            }
        }

执行业务线程的代码是这一行:

ScheduledFutureTask.super.runAndReset()

执行真正的业务线程

在runAndReset方法里,会执行真正的业务线程:java.util.concurrent.FutureTask#runAndReset

/**
     * 执行业务线程
     *
     * Executes the computation without setting its result, and then
     * resets this future to initial state, failing to do so if the
     * computation encounters an exception or is cancelled.  This is
     * designed for use with tasks that intrinsically execute more
     * than once.
     *
     * @return {@code true} if successfully run and reset
     */
    protected boolean runAndReset() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;
        boolean ran = false;
        int s = state;
        try {
            Callable<V> c = callable;
            if (c != null && s == NEW) {
                try {
                    //执行业务线程
                    c.call(); // don't set result
                    ran = true;
                } catch (Throwable ex) { //如果异常,
                    setException(ex);
                }
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        return ran && s == NEW;
    }

总结

ScheduledThreadPoolExecutor的本质是,基于线程池,实现定时任务。

定时任务的本质是,重复执行任务。