【Java原理系列】 ScheduledThreadPoolExecutor原理用法示例源码详解

本文详细介绍了ScheduledThreadPoolExecutor的原理、用法和示例。原理包括创建线程池、排队任务、延时执行等步骤;用法上它是ThreadPoolExecutor的扩展,可提交延迟或固定间隔任务;还给出了创建实例、提交延迟和固定间隔任务以及关闭执行器等示例。

ScheduledThreadPoolExecutor原理用法示例源码详解

原理

下面是ScheduledThreadPoolExecutor的原理:

  1. 创建线程池:ScheduledThreadPoolExecutor在初始化时会创建一个线程池,这个线程池用于执行任务。
  2. 排队任务:当调用ScheduledThreadPoolExecutorschedule方法来提交一个任务时,任务会被封装成一个ScheduledFutureTask,并加入到任务队列中等待执行。
  3. 延时执行:ScheduledFutureTask包含了任务的执行时间信息。ScheduledThreadPoolExecutor会根据任务的执行时间,将任务放置在延时队列中等待执行。
  4. 选择任务:线程池中的线程会不断地从延时队列中选择需要执行的任务。
  5. 执行任务:如果选择到了需要执行的任务,线程池会创建一个线程来执行该任务。
  6. 完成任务:任务执行完成后,线程池会将任务标记为已完成,并从延时队列中移除。
  7. 关闭线程池:当调用ScheduledThreadPoolExecutorshutdown方法时,线程池会停止接收新的任务,并等待已提交的任务执行完成。执行完成后,线程池会关闭。 ScheduledThreadPoolExecutor的原理可以帮助我们理解如何使用它来实现定时任务的调度。通过调整线程池的大小和任务的执行时间,我们可以实现不同的调度策略。

用法

这个类是ThreadPoolExecutor的一个扩展,它允许提交在给定延迟后运行的任务,或者按固定间隔执行的任务。与java.util.Timer相比,当需要多个工作线程时,或者当需要ThreadPoolExecutor提供的额外灵活性和功能时,这个类更有优势。

此类实现了ScheduledExecutorService接口,提供了以下功能:

  1. 延迟任务:任务可以在给定的延迟后执行,或者按固定间隔执行。
  2. FIFO顺序:任务按提交的顺序执行(First-In-First-Out)。
  3. 任务取消:可以取消正在提交的任务。取消的任务在执行前会被抑制。默认情况下,取消的任务会在其延迟过期后自动从队列中移除。
  4. 任务定制:子类可以定制用于执行命令的任务类型。
  5. 关闭行为:此类支持在关闭执行器时处理任务的不同行为。可以允许在关闭后继续运行任务,或者取消它们。

还提供了一些有用的方法,如now()、getDelay()和compareTo(),用于处理任务调度和延迟

这个类在处理需要执行定时任务或者需要按固定间隔执行任务时非常有用。

示例

下面是一些使用 ScheduledThreadPoolExecutor 的示例:

创建一个 ScheduledThreadPoolExecutor:

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);

这里我们创建了一个 ScheduledThreadPoolExecutor 实例,并设置了核心线程数为5。

提交一个延迟任务:

executor.schedule(() -> System.out.println("Task 1 executed"), 5, TimeUnit.SECONDS);

这里我们提交了一个任务,该任务在5秒后执行,并打印 “Task 1 executed”。

提交一个按固定间隔执行的任务:

executor.scheduleAtFixedRate(() -> System.out.println("Task 2 executed"), 0, 2, TimeUnit.SECONDS);

这里我们提交了一个任务,该任务每隔2秒执行一次,并打印 “Task 2 executed”。

关闭执行器并等待所有任务完成:

executor.shutdown();
try {
   
   
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
   
   
        executor.shutdownNow();
    }
} catch (InterruptedException e) {
   
   
    executor.shutdownNow();
}

这里我们首先调用 shutdown() 方法通知执行器关闭,然后等待所有任务完成。如果在60秒内所有任务都没有完成,我们将调用 shutdownNow() 方法强制关闭执行器。

源码

/**
 * A {@link ThreadPoolExecutor} that can additionally schedule
 * commands to run after a given delay, or to execute
 * periodically. This class is preferable to {@link java.util.Timer}
 * when multiple worker threads are needed, or when the additional
 * flexibility or capabilities of {@link ThreadPoolExecutor} (which
 * this class extends) are required.
 *
 * <p>Delayed tasks execute no sooner than they are enabled, but
 * without any real-time guarantees about when, after they are
 * enabled, they will commence. Tasks scheduled for exactly the same
 * execution time are enabled in first-in-first-out (FIFO) order of
 * submission.
 *
 * <p>When a submitted task is cancelled before it is run, execution
 * is suppressed. By default, such a cancelled task is not
 * automatically removed from the work queue until its delay
 * elapses. While this enables further inspection and monitoring, it
 * may also cause unbounded retention of cancelled tasks. To avoid
 * this, set {@link #setRemoveOnCancelPolicy} to {@code true}, which
 * causes tasks to be immediately removed from the work queue at
 * time of cancellation.
 *
 * <p>Successive executions of a task scheduled via
 * {@code scheduleAtFixedRate} or
 * {@code scheduleWithFixedDelay} do not overlap. While different
 * executions may be performed by different threads, the effects of
 * prior executions <a
 * href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
 * those of subsequent ones.
 *
 * <p>While this class inherits from {@link ThreadPoolExecutor}, a few
 * of the inherited tuning methods are not useful for it. In
 * particular, because it acts as a fixed-sized pool using
 * {@code corePoolSize} threads and an unbounded queue, adjustments
 * to {@code maximumPoolSize} have no useful effect. Additionally, it
 * is almost never a good idea to set {@code corePoolSize} to zero or
 * use {@code allowCoreThreadTimeOut} because this may leave the pool
 * without threads to handle tasks once they become eligible to run.
 *
 * <p><b>Extension notes:</b> This class overrides the
 * {@link ThreadPoolExecutor#execute(Runnable) execute} and
 * {@link AbstractExecutorService#submit(Runnable) submit}
 * methods to generate internal {@link ScheduledFuture} objects to
 * control per-task delays and scheduling.  To preserve
 * functionality, any further overrides of these methods in
 * subclasses must invoke superclass versions, which effectively
 * disables additional task customization.  However, this class
 * provides alternative protected extension method
 * {@code decorateTask} (one version each for {@code Runnable} and
 * {@code Callable}) that can be used to customize the concrete task
 * types used to execute commands entered via {@code execute},
 * {@code submit}, {@code schedule}, {@code scheduleAtFixedRate},
 * and {@code scheduleWithFixedDelay}.  By default, a
 * {@code ScheduledThreadPoolExecutor} uses a task type extending
 * {@link FutureTask}. However, this may be modified or replaced using
 * subclasses of the form:
 *
 *  <pre> {@code
 * public class CustomScheduledExecutor extends ScheduledThreadPoolExecutor {
 *
 *   static class CustomTask<V> implements RunnableScheduledFuture<V> { ... }
 *
 *   protected <V> RunnableScheduledFuture<V> decorateTask(
 *                Runnable r, RunnableScheduledFuture<V> task) {
 *       return new CustomTask<V>(r, task);
 *   }
 *
 *   protected <V> RunnableScheduledFuture<V> decorateTask(
 *                Callable<V> c, RunnableScheduledFuture<V> task) {
 *       return new CustomTask<V>(c, task);
 *   }
 *   // ... add constructors, etc.
 * }}</pre>
 *
 * @since 1.5
 * @author Doug Lea
 */
public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {
   
   

    /*
     * This class specializes ThreadPoolExecutor implementation by
     *
     * 1. Using a custom task type, ScheduledFutureTask for
     *    tasks, even those that don't require scheduling (i.e.,
     *    those submitted using ExecutorService execute, not
     *    ScheduledExecutorService methods) which are treated as
     *    delayed tasks with a delay of zero.
     *
     * 2. Using a custom queue (DelayedWorkQueue), a variant of
     *    unbounded DelayQueue. The lack of capacity constraint and
     *    the fact that corePoolSize and maximumPoolSize are
     *    effectively identical simplifies some execution mechanics
     *    (see delayedExecute) compared to ThreadPoolExecutor.
     *
     * 3. Supporting optional run-after-shutdown parameters, which
     *    leads to overrides of shutdown methods to remove and cancel
     *    tasks that should NOT be run after shutdown, as well as
     *    different recheck logic when task (re)submission overlaps
     *    with a shutdown.
     *
     * 4. Task decoration methods to allow interception and
     *    instrumentation, which are needed because subclasses cannot
     *    otherwise override submit methods to get this effect. These
     *    don't have any impact on pool control logic though.
     */

    /**
     * False if should cancel/suppress periodic tasks on shutdown.
     */
    private volatile boolean continueExistingPeriodicTasksAfterShutdown;

    /**
     * False if should cancel non-periodic tasks on shutdown.
     */
    private volatile boolean executeExistingDelayedTasksAfterShutdown = true;

    /**
     * True if ScheduledFutureTask.cancel should remove from queue
     */
    private volatile boolean removeOnCancel = false;

    /**
     * Sequence number to break scheduling ties, and in turn to
     * guarantee FIFO order among tied entries.
     */
    private static final AtomicLong sequencer = new AtomicLong();

    /**
     * Returns current nanosecond time.
     */
    final long now() {
   
   
        return System.nanoTime();
    }

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

    /**
     * Returns true if can run a task given current run state
     * and run-after-shutdown parameters.
     *
     * @param periodic true if this task periodic, false if delayed
     */
    boolean canRunInCurrentRunState(boolean periodic) {
   
   
        return isRunningOrShutdown(periodic ?
                                   continueExistingPeriodicTasksAfterShutdown :
                                   executeExistingDelayedTasksAfterShutdown);
    }

    /**
     * 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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值