• Timer和ScheduledThreadPoolExecutor的区别及源码分析


    Timer

    基于单线程系统时间实现的延时、定期任务执行类。具体可以看下面红色标注的代码。

    public class Timer {
        /**
         * The timer task queue.  This data structure is shared with the timer
         * thread.  The timer produces tasks, via its various schedule calls,
         * and the timer thread consumes, executing timer tasks as appropriate,
         * and removing them from the queue when they're obsolete.
         */
        private final TaskQueue queue = new TaskQueue();
    
        /**
         * The timer thread.*/
        private final TimerThread thread = new TimerThread(queue);
    class TimerThread extends Thread {
        /**
         * This flag is set to false by the reaper to inform us that there
         * are no more live references to our Timer object.  Once this flag
         * is true and there are no more tasks in our queue, there is no
         * work left for us to do, so we terminate gracefully.  Note that
         * this field is protected by queue's monitor!
         */
        boolean newTasksMayBeScheduled = true;
    
        /**
         * Our Timer's queue.  We store this reference in preference to
         * a reference to the Timer so the reference graph remains acyclic.
         * Otherwise, the Timer would never be garbage-collected and this
         * thread would never go away.
         */
        private TaskQueue queue;
    
        TimerThread(TaskQueue queue) {
            this.queue = queue;
        }
    
        public void run() {
            try {
                mainLoop();
            } finally {
                // Someone killed this Thread, behave as if Timer cancelled
                synchronized(queue) {
                    newTasksMayBeScheduled = false;
                    queue.clear();  // Eliminate obsolete references
                }
            }
        }
    
        /**
         * The main timer loop.  (See class comment.)
         */
        private void mainLoop() {
            while (true) {
                try {
                    TimerTask task;
                    boolean taskFired;
                    synchronized(queue) {
                        // Wait for queue to become non-empty
                        while (queue.isEmpty() && newTasksMayBeScheduled)
                            queue.wait();
                        if (queue.isEmpty())
                            break; // Queue is empty and will forever remain; die
    
                        // Queue nonempty; look at first evt and do the right thing
                        long currentTime, executionTime;
                        task = queue.getMin();
                        synchronized(task.lock) {
                            if (task.state == TimerTask.CANCELLED) {
                                queue.removeMin();
                                continue;  // No action required, poll queue again
                            }
                            currentTime = System.currentTimeMillis();
                            executionTime = task.nextExecutionTime;
                            if (taskFired = (executionTime<=currentTime)) {
                                if (task.period == 0) { // Non-repeating, remove
                                    queue.removeMin();
                                    task.state = TimerTask.EXECUTED;
                                } else { // Repeating task, reschedule
                                    queue.rescheduleMin(
                                      task.period<0 ? currentTime   - task.period
                                                    : executionTime + task.period);
                                }
                            }
                        }
                        if (!taskFired) // Task hasn't yet fired; wait
                            queue.wait(executionTime - currentTime);
                    }
                    if (taskFired)  // Task fired; run it, holding no locks
                        task.run();
                } catch(InterruptedException e) {
                }
            }
        }
    }

     Timer延时、定时任务的实现采用单线程,在主循环(mainLoop)中循环遍历任务队列(TaskQueue),如果执行时间小于等于当前系统时间则执行任务,否则继续等待(执行时间-当前时间)。

    ScheduledThreadPoolExecutor

    基于多线程、JVM时间实现的延时、定期任务执行类。具体可以看下面红色标注的代码。

     public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue());
        }
     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;
        }
     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);
            else if (wc == 0)
                addWorker(null, false);
        }
    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;
                    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) {
                        t.start();
                        workerStarted = true;
                    }
                }
            } finally {
                if (! workerStarted)
                    addWorkerFailed(w);
            }
            return workerStarted;
        }
    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();
                    // If pool is stopping, ensure thread is interrupted;
                    // if not, ensure thread is not interrupted.  This
                    // requires a recheck in second case to deal with
                    // shutdownNow race while clearing interrupt
                    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;
                        w.completedTasks++;
                        w.unlock();
                    }
                }
                completedAbruptly = false;
            } finally {
                processWorkerExit(w, completedAbruptly);
            }
        }
     private Runnable getTask() {
            boolean timedOut = false; // Did the last poll() time out?
    
            for (;;) {
                int c = ctl.get();
                int rs = runStateOf(c);
    
                // Check if queue empty only if necessary.
                if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                    decrementWorkerCount();
                    return null;
                }
    
                int wc = workerCountOf(c);
    
                // Are workers subject to culling?
                boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
    
                if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                    if (compareAndDecrementWorkerCount(c))
                        return null;
                    continue;
                }
    
                try {
                    Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                    if (r != null)
                        return r;
                    timedOut = true;
                } catch (InterruptedException retry) {
                    timedOut = false;
                }
            }
        }
    private class ScheduledFutureTask<V>
                extends FutureTask<V> implements RunnableScheduledFuture<V> {
    
            /** Sequence number to break ties FIFO */
            private final long sequenceNumber;
    
            /** The time the task is enabled to execute in nanoTime units */
            private long time;
    
            /**
             * Period in nanoseconds for repeating tasks.  A positive
             * value indicates fixed-rate execution.  A negative value
             * indicates fixed-delay execution.  A value of 0 indicates a
             * non-repeating task.
             */
            private final long period;
    
            /** The actual task to be re-enqueued by reExecutePeriodic */
            RunnableScheduledFuture<V> outerTask = this;
    
            /**
             * Index into delay queue, to support faster cancellation.
             */
            int heapIndex;
    
            /**
             * Creates a one-shot action with given nanoTime-based trigger time.
             */
            ScheduledFutureTask(Runnable r, V result, long ns) {
                super(r, result);
                this.time = ns;
                this.period = 0;
                this.sequenceNumber = sequencer.getAndIncrement();
            }
    
            /**
             * Creates a periodic action with given nano time and period.
             */
            ScheduledFutureTask(Runnable r, V result, long ns, long period) {
                super(r, result);
                this.time = ns;
                this.period = period;
                this.sequenceNumber = sequencer.getAndIncrement();
            }
    
            /**
             * Creates a one-shot action with given nanoTime-based trigger time.
             */
            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) // compare zero if same object
                    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;
            }
    
            /**
             * Returns {@code true} if this is a periodic (not a one-shot) action.
             *
             * @return {@code true} if periodic
             */
            public boolean isPeriodic() {
                return period != 0;
            }
    
            /**
             * Sets the next time to run for a periodic task.
             */
            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;
            }
    
            /**
             * 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);
                }
            }
        }

    ScheduledThreadPoolExecutor执行流程总结:

    1.schedule(定期执行方法)

    2.new Task(ScheduledFutureTask)(构建任务作业)

    3.delayedExecute(task) (延时执行任务)

    4.workQueue.add(task) (任务作业添加到阻塞队列)

    5.addWorker(null, true or false) (添加核心作业线程,当核心线程数设置为0时则启动一个非核心线程)

    6.runWorker(运行作业线程)

    7.循环:getTask ->workQueue.take(获取作业)

    8.task.run (作业运行)->ScheduledFutureTask.run(reExecutePeriodic(outerTask)-> workQueue.add(task)......) (周期作业重复调用)

    核心线程 或 非核心线程循环从队列中获取Task执行,周期任务则将任务重新排队

    DelayedWorkQueue中的take方法

    public RunnableScheduledFuture<?> take() throws InterruptedException {
                final ReentrantLock lock = this.lock;
                lock.lockInterruptibly();
                try {
                    for (;;) {
                        RunnableScheduledFuture<?> first = queue[0];
                        if (first == null)
                            available.await();
                        else {
                            long delay = first.getDelay(NANOSECONDS);
                            if (delay <= 0)
                                return finishPoll(first);
                            first = null; // don't retain ref while waiting
                            if (leader != null)
                                available.await();
                            else {
                                Thread thisThread = Thread.currentThread();
                                leader = thisThread;
                                try {
                                    available.awaitNanos(delay);
                                } finally {
                                    if (leader == thisThread)
                                        leader = null;
                                }
                            }
                        }
                    }
                } finally {
                    if (leader == null && queue[0] != null)
                        available.signal();
                    lock.unlock();
                }
            }
    public long getDelay(TimeUnit unit) {
                return unit.convert(time - now(), NANOSECONDS);
            }
        /**
         * Returns current nanosecond time.
         */
        final long now() {
            return System.nanoTime();
        }

    ScheduledThreadPoolExecutor执行延时、定期任务,核心代码就在runWorker,循环获取任务队列中的任务然后执行,在获取任务的时候如果任务的执行时间没到,则进行等待。延时时间的计算都是基于System.nanoTime(),即JVM时间。

    ThreadPoolExecutor执行流程(参考)

    submit(task)-> execute(task)
    ->1 当前线程数<核心线程数:addWorker(task,true) (添加核心工作者线程,任务并没有进入队列排队)->runWorker-> task.run (核心线程直接执行任务)
    ->2 当前线程数>=核心线程数:workQueue.add(task) (任务作业添加到阻塞队列)->

      2.1 任务排队成功:addWorker(null, false)(非核心工作者线程)-> 循环【getTask(workQueue.take)->task.run】(非核心线程循环从队列中获取Task执行

      2.2 任务排队失败:addWorker(task, false) (非核心工作者线程)-> task.run (尝试添加非核心线程执行任务

    优缺点:

    1.Timer单线程,执行周期任务时,一次出错,则TimerThread线程终止, 所有任务将无法执行。而且任务的执行时间可能会影响周期的准确性。

    2.Timer基于系统时间,系统时间的修改会影响任务的执行。在以系统时间为准的场景中(public void schedule(TimerTask task, Date time))使用非常合适,使用周期性任务则受到极大影响,因为时间间隔被破坏!

    3.ScheduledThreadPoolExecutor多线程,任务的执行不会相互影响,且能保证执行时间间隔的准确性。

    4.ScheduledThreadPoolExecutor基于JVM时间,该时间本身无任何意义,仅用来计算时间间隔,不受系统时间影响。所以用来计算周期间隔特别合适,而且单位是纳秒更加精确。因此延时任务、周期任务采用它比Timer更加靠谱!

    总结:

    Timer的使用场景,仅在基于系统时间为准的场景中非常合适(依赖当前系统时间进行判断任务的执行)。

    ScheduledThreadPoolExecutor的使用场景则更为广泛,对延时任务、周期任务使用此类更靠谱(依赖时间间隔(JVM时间差值计算得到)进行判断任务的执行)。基于系统时间执行的任务则无法精确(因为系统时间可以随时调整)!

  • 相关阅读:
    nginx
    DNS
    lrzsz上传下载命令
    linux命令大全20180614
    解决Nginx的connect() to 127.0.0.1:8080 failed (13: Permission denied) while connect
    安装nginx
    scp复制
    Vue3中报错“export ‘createWebHistory, createRouter‘ was not found in ‘vue-router‘
    点击div,span, p这些文本标签时隐藏光标的方法
    el-upload使用beforeUpload不生效
  • 原文地址:https://www.cnblogs.com/hdwang/p/16436142.html
Copyright © 2020-2023  润新知