深入理解高并发编程 - 深度解析ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor 并实现了 ScheduledExecutorService 接口,这使得它可以同时充当线程池和定时任务调度器。

构造方法

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}

构造方法接收一个 corePoolSize 参数,它表示线程池中的核心线程数。核心线程是一直保持存活的线程,即使没有任务执行,以便支持定时任务的调度。

构造方法的参数及其含义如下:

corePoolSize:线程池中的核心线程数。这些核心线程会一直保持存活,即使没有任务需要执行。核心线程数决定了线程池的基本能力,可以处理的并发任务数为核心线程数。

Integer.MAX_VALUE:线程池的最大线程数。这里设置为整数的最大值,表示线程池没有最大线程数限制。当任务数量超过核心线程数时,新的任务会被排队到队列中等待执行。

0:非核心线程的闲置存活时间。对于非核心线程(超过核心线程数的线程),如果没有任务需要执行,它们在闲置一段时间后会被回收。

NANOSECONDS:时间单位,表示使用纳秒作为时间单位。

new DelayedWorkQueue():构造一个 DelayedWorkQueue 对象作为任务队列。DelayedWorkQueue 是一个延迟队列,用于存储定时任务,根据任务的调度时间进行排序。

在这个构造方法中,ScheduledThreadPoolExecutor 将核心线程数、最大线程数、任务队列等参数传递给其父类 ThreadPoolExecutor 的构造方法,从而创建一个具有定时任务调度能力的线程池。

schedule 方法

ScheduledThreadPoolExecutor 提供了一系列 schedule 方法,用于调度定时任务的执行。这些方法允许指定任务的延迟时间,任务会在延迟时间过后执行一次。以下是 schedule 方法的签名和解析:

schedule(Runnable command, long delay, TimeUnit unit):

这个方法用于调度一个 Runnable 任务,在指定的延迟时间后执行一次。返回一个 ScheduledFuture 对象,可以使用它来取消任务或者获取任务执行的结果。

command:要执行的任务。
delay:延迟时间。
unit:时间单位。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Task executed after delay");
ScheduledFuture<?> future = scheduler.schedule(task, 5, TimeUnit.SECONDS);

<V> schedule(Callable<V> callable, long delay, TimeUnit unit):

这个方法与上一个方法类似,不同之处在于它可以调度一个带有返回值的 Callable 任务。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Callable<String> task = () -> "Task result";
ScheduledFuture<String> future = scheduler.schedule(task, 3, TimeUnit.SECONDS);

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):

这个方法用于调度一个 Runnable 任务,在初始延迟时间后开始执行,然后每隔一定的时间间隔执行一次。任务的执行时间不考虑,每次任务会尽量在指定的时间间隔后执行。

command:要执行的任务。
initialDelay:初始延迟时间。
period:时间间隔。
unit:时间单位。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Task executed at fixed rate");
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);

scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):

这个方法与上一个方法类似,不同之处在于任务的执行间隔是基于任务完成的时间,而不是固定的时间间隔。任务完成后,会等待一段时间(delay)再执行下一次。

command:要执行的任务。
initialDelay:初始延迟时间。
delay:间隔时间。
unit:时间单位。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Task executed with fixed delay");
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(task, 2, 3, TimeUnit.SECONDS);

这些 schedule 方法可以方便地调度不同类型的定时任务,从简单的延迟执行到周期性执行,以满足各种需求。

decorateTask 方法

在 ScheduledThreadPoolExecutor 中,decorateTask 方法用于对任务进行修饰,以支持定时任务的调度。它会将普通的 Runnable 或 Callable 任务包装成 ScheduledFutureTask 对象,这样可以将任务调度相关的信息添加到任务中,如延迟时间、调度时间等。以下是 decorateTask 方法的部分实现:

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
    return task;
}

protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) {
    return task;
}

这两个方法分别用于对 Runnable 和 Callable 任务进行修饰。它们的作用是将传入的任务(runnable 或 callable)与现有的 RunnableScheduledFuture 任务对象进行合并,从而创建一个新的 RunnableScheduledFuture 对象。

decorateTask 方法默认的实现是将传入的任务不做任何修饰,直接返回原来的任务对象。这是因为大部分情况下,任务并不需要额外的修饰。但是,也可以在自定义的子类中覆盖这个方法,以实现自己的修饰逻辑。

这个方法的存在使得 ScheduledThreadPoolExecutor 可以在任务执行前对任务对象进行一些预处理操作,以满足不同的调度需求。通常情况下,不需要直接调用这个方法,它会在 schedule 方法内部自动调用。

scheduleAtFixedRate 方法

scheduleAtFixedRate 方法用于调度一个任务,在初始延迟时间后开始执行,然后每隔一定的时间间隔执行一次。任务的执行时间不考虑,每次任务会尽量在指定的时间间隔后执行。以下是 scheduleAtFixedRate 方法的签名和解析:

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

参数说明:

command:要执行的任务,即一个实现了 Runnable 接口的对象。
initialDelay:初始延迟时间,表示从调度开始到第一次执行的时间间隔。
period:任务执行的周期间隔。
unit:时间单位。
返回值:ScheduledFuture 对象,表示任务的调度结果。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

Runnable task = () -> System.out.println("Task executed at fixed rate");

// 初始延迟 1 秒,周期间隔 2 秒
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);

在上述示例中,任务 task 首次会在 1 秒后执行,然后每隔 2 秒执行一次。需要注意的是,scheduleAtFixedRate 方法不会考虑任务的执行时间。如果任务的执行时间超过指定的周期间隔,后续任务会尽量在每个周期的开始时执行,但可能会累积延迟。因此,如果任务的执行时间很长,可能会影响后续任务的调度,导致调度的间隔变得不精确。

总之,scheduleAtFixedRate 方法适用于需要以固定的时间间隔调度任务,而不考虑任务的执行时间。

scheduleWithFixedDelay 方法

scheduleWithFixedDelay 方法用于调度一个任务,在初始延迟时间后开始执行,然后每次任务完成后会等待一段时间,再执行下一次。任务的执行间隔是基于任务完成的时间,而不是固定的时间间隔。以下是 scheduleWithFixedDelay 方法的签名和解析:

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

参数说明:

command:要执行的任务,即一个实现了 Runnable 接口的对象。
initialDelay:初始延迟时间,表示从调度开始到第一次执行的时间间隔。
delay:每次任务执行后的等待时间,即任务完成后等待多长时间再执行下一次。
unit:时间单位。
返回值:ScheduledFuture 对象,表示任务的调度结果。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

Runnable task = () -> System.out.println("Task executed with fixed delay");

// 初始延迟 2 秒,每次任务执行后等待 3 秒再执行
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(task, 2, 3, TimeUnit.SECONDS);

在上述示例中,任务 task 首次会在 2 秒后执行,然后每次任务完成后会等待 3 秒,再执行下一次。这个方法适用于需要在任务执行完毕后等待一段时间再进行下一次调度的情况,可以避免任务的执行时间对后续任务的调度造成影响。

需要注意的是,scheduleWithFixedDelay 方法会考虑任务的执行时间,确保每次任务的执行间隔都是基于上一次任务完成的时间。这样可以保证在任何情况下,任务之间的时间间隔都保持相对稳定。

triggerTime 方法

onShutdown 方法用于在线程池关闭时执行特定的清理操作。以下是 onShutdown 方法的签名和解析:

protected void onShutdown() {
    // 默认的实现为空
}

这个方法是一个钩子方法(hook method),在线程池执行 shutdown 或 shutdownNow 方法时会被调用。默认情况下,onShutdown 方法没有实际的实现,即空方法。但是可以通过继承 ScheduledThreadPoolExecutor 并覆盖 onShutdown 方法,来自定义在线程池关闭时的操作。

在自定义实现中,可以进行一些资源释放、清理工作,或者记录日志等操作,以确保线程池的正常关闭和资源的释放。例如:

class CustomScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {

public CustomScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize);
}

@Override
protected void onShutdown() {
    // 在线程池关闭时执行特定的清理操作
    System.out.println("CustomThreadPoolExecutor is shutting down...");
    // 执行其他清理操作...
    super.onShutdown();
}

}

通过覆盖 onShutdown 方法,可以在线程池关闭时执行自定义的清理逻辑,从而确保应用程序的资源管理和退出行为。

相关推荐
苹果酱05676 分钟前
C语言 char 字符串 - C语言零基础入门教程
java·开发语言·spring boot·mysql·中间件
csucoderlee13 分钟前
eclipse mat leak suspects report和 component report的区别
java·ide·eclipse
代码小鑫23 分钟前
A032-基于Spring Boot的健康医院门诊在线挂号系统
java·开发语言·spring boot·后端·spring·毕业设计
训山30 分钟前
4000字浅谈Java网络编程
java·开发语言·网络
VertexGeek37 分钟前
Rust学习(四):作用域、所有权和生命周期:
java·学习·rust
喔喔咿哈哈1 小时前
【手撕 Spring】 -- Bean 的创建以及获取
java·后端·spring·面试·开源·github
码农小丘1 小时前
了解springboot国际化用途以及使用
java·spring boot·spring
卡皮巴拉吖1 小时前
【JavaEE初阶】多线程上部
java·jvm·java-ee
tian-ming1 小时前
JavaWeb后端开发知识储备1
java·spring boot·nginx·spring·maven
spy47_1 小时前
JavaEE 重要的API阅读
java·笔记·java-ee·api文档阅读