ScheduledThreadPoolExecutor 是 Java 中的一个线程池实现,它继承自 ThreadPoolExecutor 类,并实现了 ScheduledExecutorService 接口,ScheduledThreadPoolExecutor内部维护了一个任务队列和一组线程,可以执行周期性和延迟性任务,并且可以动态地调整线程池的大小。
ScheduledThreadPoolExecutor 的核心原理是:
总之,ScheduledThreadPoolExecutor 通过线程池和任务队列来管理任务的执行,并通过 DelayQueue 和 ScheduledFutureTask 来实现任务的延迟执行和周期性执行。它具有线程池的优点,可以复用线程、控制线程数量等,同时也支持延迟执行和周期性执行,适用于需要执行定时任务的场景。
使用 ScheduledThreadPoolExecutor,需要按照以下步骤进行操作:
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, handler);
// 这个任务将在指定的延迟时间之后执行一次。
executor.schedule(() -> {
// 执行任务
},
delay, // 任务的延迟时间
TimeUnit.MILLISECONDS // 时间单位
);
// 周期性地执行任务【这个任务将在 initialDelay 时间之后开始执行,然后每隔 period 时间执行一次。】
executor.scheduleAtFixedRate((Runnable) () -> {
// 执行任务
},
initialDelay, // 任务的初始延迟时间
period, // 任务的周期时间
TimeUnit.MILLISECONDS // 表示时间单位
);
ScheduledFuture<?> future = executor.schedule(() -> {
// 执行任务
},
delay, // 任务的延迟时间
TimeUnit.MILLISECONDS // 时间单位
);
// 取消任务
future.cancel(true);
executor.shutdown();
需要注意的是,ScheduledThreadPoolExecutor 中的周期性任务执行时间不能超过指定的时间间隔,否则会影响下一次任务的启动时间。此外,在任务的实现中,也需要注意线程安全问题,避免对共享资源的竞争和冲突。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPoolExecutor(int corePoolSize)构造方法,用于创建一个可调度的线程池。该线程池会在指定时间内执行给定的任务,并且可以按照指定的时间间隔重复执行任务。
参数说明:
ScheduledThreadPoolExecutor(int corePoolSize)构造方法调用其父类(ThreadPoolExecutor)构造方法,参数含义如下:
该构造方法继承自 ThreadPoolExecutor类的构造方法,因此ScheduledThreadPoolExecutor实际上是基于 ThreadPoolExecutor实现的,它添加了调度功能,能够支持延迟执行和周期性执行任务。
注意:由于maximumPoolSize设置是Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory),用于创建一个可调度的线程池。该线程池会在指定时间内执行给定的任务,并且可以按照指定的时间间隔重复执行任务。
参数说明:
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory)构造方法调用其父类 ThreadPoolExecutor 构造方法,参数含义如下:
注意:由于maximumPoolSize设置是Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
}
ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler)构造函数,用于创建一个可调度的线程池。该线程池会在指定时间内执行给定的任务,并且可以按照指定的时间间隔重复执行任务。
参数说明:
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory)构造方法调用其父类 ThreadPoolExecutor 构造方法,参数含义如下:
构造函数中,maximumPoolSize被设置为 Integer.MAX_VALUE,表示线程池中的线程数没有上限,这是因为该线程池是一个可调度的线程池,任务执行的时间不确定,因此无法确定线程池的最大线程数。同时,使用了一个 DelayedWorkQueue作为任务队列,该队列可以按照任务延迟的时间进行排序,从而实现任务的定时执行。而线程池中的拒绝策略则由传入的 handler参数决定,用于处理任务提交时无法处理的任务,例如队列已满或者线程池已经关闭等情况。
注意:由于maximumPoolSize设置是Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler)构造函数,用于创建一个可调度的线程池。该线程池会在指定时间内执行给定的任务,并且可以按照指定的时间间隔重复执行任务。
参数说明:
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler)构造函数构造方法调用其父类 ThreadPoolExecutor 构造方法,参数含义如下:
注意:由于maximumPoolSize设置是Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
ScheduledFutureTask 继承自 FutureTask,同时实现了 RunnableScheduledFuture 接口,提供了定时执行和周期性执行任务的能力。
在 ScheduledFutureTask 中,任务的执行时间可以通过构造函数或者 setTime 方法来设置,任务的执行时间可以是固定的时间点,也可以是相对时间,例如在当前时间的基础上延迟一段时间后执行。任务的周期性执行可以通过 setPeriod`方法来设置,当任务执行完成后,会在指定的时间间隔后重新执行。
该类的主要方法包括:
ScheduledFutureTask 类是 ScheduledThreadPoolExecutor类中使用的任务类,用于实现定时任务和周期性任务的调度和执行。在 ScheduledThreadPoolExecutor 中,任务会被封装成 ScheduledFutureTask 对象,然后加入任务队列中等待执行。在任务执行时,会调用 ScheduledFutureTask的 run方法来执行任务,当任务执行完成后,如果是周期性任务,会重新计算下一次执行时间,并将任务重新加入任务队列中等待执行。
DelayedWorkQueue实现了 BlockingQueue接口。它用于存储那些被延迟执行的任务,并根据任务的延迟时间进行排序。延迟时间短的任务排在队列的前面,这样就能保证队列头部的任务是最近需要执行的任务。
DelayedWorkQueue 中的每个元素都是一个 ScheduledFutureTask 对象,包含了一个要被执行的任务以及它的延迟时间。在每个任务被加入队列时,DelayedWorkQueue 会根据这个延迟时间将任务插入到适当的位置。当队列头部的任务的延迟时间已经过去时,它就会被取出并执行。
需要注意的是,DelayedWorkQueue 是一个有界队列,其容量由 ScheduledThreadPoolExecutor构造函数中传入的 queueSize 参数决定。如果队列已满,那么新的任务将无法加入队列中,直到有任务被取出并执行。如果队列中的任务全部都是周期性任务(通过 scheduleAtFixedRate 或 scheduleWithFixedDelay方法添加),并且这些任务的周期时间都很短,那么队列中可能会出现任务堆积的情况,这时就需要适当地调整队列的容量。
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay,
TimeUnit unit) {
if (callable == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
Scheduled方法是延迟一定时间(delay)后执行一个任务(callable),ScheduledFutureTask对象会被添加到DelayedWorkQueue中,ScheduledThreadPoolExecutor会在任务到达执行时间时从队列中取出任务并执行。如果任务执行过程中发生异常,ScheduledThreadPoolExecutor会根据任务的重试次数和重试间隔重新将任务添加到DelayedWorkQueue中,等待下次执行。需要注意的是,如果任务的执行时间超过了设定的延迟时间,那么任务会立即开始执行,不会等待当前执行结束。返回的ScheduledFuture对象可以用于取消。
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;
}
scheduleAtFixedRate方法是将一个任务按照固定频率(period)执行,每次执行的时间间隔为period,第一次执行的延迟时间为initialDelay。ScheduledFutureTask对象会被添加到DelayedWorkQueue中,ScheduledThreadPoolExecutor会在任务到达执行时间时从队列中取出任务并执行。如果任务执行过程中发生异常,ScheduledThreadPoolExecutor会根据任务的重试次数和重试间隔重新将任务添加到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;
}
scheduleWithFixedDelay方法是将一个任务按照固定延迟时间(delay)多次执行,每次执行的时间间隔为delay,第一次执行的延迟时间为initialDelay。ScheduledFutureTask对象会被添加到DelayedWorkQueue中,ScheduledThreadPoolExecutor会在任务到达执行时间时从队列中取出任务并执行。如果任务执行过程中发生异常,ScheduledThreadPoolExecutor会根据任务的重试次数和重试间隔重新将任务添加到DelayedWorkQueue中,等待下次执行。
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();
}
}
delayedExecute方法是将任务添加到DelayedWorkQueue中,并确保线程池中至少有一个线程在运行。如果ScheduledThreadPoolExecutor已经关闭并且任务不满足当前执行状态,那么任务将被取消。需要注意的是,DelayedWorkQueue是一个有序队列,按照任务的执行时间排序,因此任务会按照执行时间的顺序执行。另外,当任务执行时,ScheduledThreadPoolExecutor会将任务包装成FutureTask对象并提交给ThreadPoolExecutor进行执行。