在 Java 开发中,任务调度是一个常见的需求。java.util.concurrent.ScheduledExecutorService 为我们提供了强大的任务调度功能。通常,我们可以在代码初始化时就定义好任务及其调度规则,但有时我们需要在运行时动态地添加任务调度,本文将详细介绍如何在 Java 中实现动态添加 Scheduled 任务。

一、ScheduledExecutorService 基础回顾

ScheduledExecutorService 是一个基于线程池的调度框架。它可以按照固定的延迟、固定的速率或者在特定的时间点来执行任务。

首先,创建一个 ScheduledExecutorService 实例:

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

这里创建了一个包含 5 个线程的线程池用于任务调度。

然后,可以使用 schedule 方法来安排一个一次性任务在指定延迟后执行,例如:

Runnable task = () -> System.out.println("This is a scheduled task.");
scheduledExecutorService.schedule(task, 5, TimeUnit.SECONDS);

上述代码将在 5 秒后执行 task 任务。

如果要按照固定的延迟重复执行任务,可以使用 scheduleWithFixedDelay 方法:

Runnable repeatingTask = () -> System.out.println("Repeating task at fixed delay.");
scheduledExecutorService.scheduleWithFixedDelay(repeatingTask, 0, 2, TimeUnit.SECONDS);

此任务将在初始延迟 0 秒后开始执行,然后每次执行完成后等待 2 秒再次执行。

使用 scheduleAtFixedRate 方法则可以按照固定的速率执行任务:

Runnable fixedRateTask = () -> System.out.println("Task running at fixed rate.");
scheduledExecutorService.scheduleAtFixedRate(fixedRateTask, 0, 3, TimeUnit.SECONDS);

它会尽量保证任务按照固定的时间间隔(这里是 3 秒)执行,即使前一个任务执行时间较长,也会尽快启动下一个任务。

最后,当不再需要任务调度时,记得关闭 ScheduledExecutorService

scheduledExecutorService.shutdown();

二、动态添加 Scheduled 任务的实现

在实际应用中,可能需要根据用户的操作、系统的运行状态等动态地添加任务调度。以下是一个简单的示例场景:我们有一个监控系统,当某个指标超过阈值时,动态添加一个任务来处理异常情况。

首先,创建一个用于存储任务的集合:

Set<ScheduledFuture<?>> scheduledTasks = new HashSet<>();

然后,定义一个方法来动态添加任务:

public void addScheduledTask(ScheduledExecutorService executor, Runnable task, long initialDelay, long delay, TimeUnit unit) {
    ScheduledFuture<?> future = executor.scheduleWithFixedDelay(task, initialDelay, delay, unit);
    scheduledTasks.add(future);
}

在这个方法中,我们将新创建的任务的 ScheduledFuture 对象添加到集合中,以便后续管理(例如取消任务)。

假设我们有一个条件判断,当满足某个条件时添加任务:

if (someCondition) {
    Runnable dynamicTask = () -> System.out.println("Dynamic scheduled task triggered.");
    addScheduledTask(scheduledExecutorService, dynamicTask, 0, 5, TimeUnit.SECONDS);
}

someCondition 为真时,就会动态添加一个任务,该任务每 5 秒执行一次,从添加时立即开始(初始延迟 0 秒)。

三、任务的管理与取消

由于我们将任务的 ScheduledFuture 对象存储在集合中,所以可以方便地对任务进行管理。例如,要取消所有已添加的任务:

public void cancelAllTasks() {
    for (ScheduledFuture<?> task : scheduledTasks) {
        task.cancel(true);
    }
    scheduledTasks.clear();
}

这里通过遍历集合,调用每个任务的 cancel 方法来取消任务,并清空集合。

四、注意事项

  • 动态添加任务时,要确保线程池有足够的资源来处理新添加的任务,避免资源耗尽。
  • 在取消任务时,要考虑任务执行过程中的资源清理等操作,尤其是任务涉及到数据库操作、文件操作等资源占用情况。
  • 对于长时间运行的任务,要合理设置任务的超时时间或者采用合适的监控机制,防止任务阻塞线程池,影响其他任务的调度。

通过以上介绍,我们可以在 Java 应用中灵活地实现动态添加 Scheduled 任务,根据实际需求更好地构建任务调度逻辑,提高系统的灵活性和适应性。