ScheduledThreadPoolExecutor 是 Java 中的一种线程池,它继承自 ThreadPoolExecutor,并实现了 ScheduledExecutorService 接口。它用于调度和执行定时任务,支持延时执行和周期性执行。

相比于普通的 ThreadPoolExecutorScheduledThreadPoolExecutor 提供了更多的定时任务调度功能,适用于执行需要按固定时间间隔、延时执行的任务。

1. 主要特点

  • 延时执行:任务可以在指定的延时后执行。
  • 周期执行:任务可以按照固定的周期反复执行。
  • 线程池管理:继承自 ThreadPoolExecutor,它能够有效管理线程池中的线程。
  • 支持取消任务:你可以取消一个任务的执行。

2. 构造方法

ScheduledThreadPoolExecutor 提供了以下几种常用的构造方法:

public ScheduledThreadPoolExecutor(int corePoolSize);
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory);
public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler);
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler);
  • corePoolSize:线程池的核心线程数。线程池的核心线程数大小决定了线程池在不忙时保留的线程数。
  • threadFactory:自定义线程工厂,用于创建新线程。
  • handler:拒绝执行任务时的处理策略。

3. 核心方法

ScheduledThreadPoolExecutor 提供了一些方法来安排任务的执行,包括延时执行、周期性执行等。

3.1 延时执行

ScheduledThreadPoolExecutor 可以调度任务在某个延迟之后执行。

  • schedule():将任务安排在某个延时后执行一次。
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
Runnable task = () -> System.out.println("Task executed!");
executor.schedule(task, 5, TimeUnit.SECONDS);  // 5秒后执行

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

3.2 周期性执行

你可以让任务按照固定的时间间隔周期性执行。

  • scheduleAtFixedRate():按照固定的频率执行任务。它指定了两次执行任务之间的时间间隔,但不会考虑任务执行的时间长度。
executor.scheduleAtFixedRate(() -> System.out.println("Fixed-rate task executed!"), 0, 10, TimeUnit.SECONDS);

上述代码将在立即启动任务后,每 10 秒执行一次任务。scheduleAtFixedRate 保证了两次任务之间的间隔时间是固定的。

  • scheduleWithFixedDelay():与 scheduleAtFixedRate() 类似,但是它会考虑任务的执行时间。如果任务执行的时间较长,下一次任务会在上一次执行完毕之后再开始,并且两次任务之间的间隔是以执行时间为基准。
executor.scheduleWithFixedDelay(() -> System.out.println("Fixed-delay task executed!"), 0, 10, TimeUnit.SECONDS);

上述代码将在立即启动任务后,每次执行完任务后等待 10 秒再执行下一次任务。

3.3 调度周期性任务的区别

  • scheduleAtFixedRate()
    • 任务的开始时间是固定的间隔(例如,每 10 秒执行一次)。
    • 即使任务的执行时间较长,也不会延迟下次执行。
  • scheduleWithFixedDelay()
    • 每次任务的开始时间是上一次任务执行完成后的固定延时(例如,每次任务执行完 10 秒后才开始下次执行)。
    • 任务的执行时间会影响到下一次任务的调度。

4. 如何工作

  1. 任务调度
    ScheduledThreadPoolExecutor 继承了 ThreadPoolExecutor,所以它的任务执行过程与 ThreadPoolExecutor 类似。调度线程池通过一个内部的调度器来管理任务的执行。调度器会定期检查是否有需要执行的任务。
  2. 延时任务
    当任务被安排延时执行时,调度器会将任务放入任务队列,并在指定的延迟时间后提交给线程池执行。
  3. 周期性任务
    周期性任务被安排后,调度器会根据任务的类型(FixedRate 或 FixedDelay)计算下次任务的执行时间,并将其提交给线程池。

5. 任务取消

你可以使用 ScheduledFuture 来取消已调度的任务。

  • cancel():取消任务。
ScheduledFuture<?> future = executor.schedule(() -> System.out.println("Task executed!"), 5, TimeUnit.SECONDS);
future.cancel(false);  // 取消任务
  • get():获取任务的执行结果。
ScheduledFuture<?> future = executor.schedule(() -> System.out.println("Task executed!"), 5, TimeUnit.SECONDS);
future.get();  // 阻塞,直到任务执行完成

6. 常见应用场景

  • 定时任务:比如定时清理缓存、定时报告生成、定时备份等。
  • 周期性任务:比如定时轮询某个服务、定时检查任务执行情况等。
  • 延时任务:比如延时发送通知、延时执行某些操作等。

7. 示例代码

import java.util.concurrent.*;

public class ScheduledThreadPoolExecutorExample {
    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);

        // 延时任务
        executor.schedule(() -> System.out.println("Delayed Task Executed!"), 3, TimeUnit.SECONDS);

        // 周期性任务:每 2 秒执行一次
        executor.scheduleAtFixedRate(() -> System.out.println("Fixed-rate Task Executed!"), 1, 2, TimeUnit.SECONDS);

        // 周期性任务:每次任务结束后等待 2 秒再执行
        executor.scheduleWithFixedDelay(() -> System.out.println("Fixed-delay Task Executed!"), 1, 2, TimeUnit.SECONDS);
    }
}

8. 注意事项

  • 线程池的大小ScheduledThreadPoolExecutor 维护了一个核心线程池,因此你需要合理配置核心线程数。
  • 任务的执行时间:对于周期性任务,若任务执行时间过长,可能导致任务间隔时间不准。因此,使用 scheduleWithFixedDelay 比较合适。
  • 取消任务:尽量避免使用过多的 cancel() 操作,否则会影响性能。
  • 拒绝策略:与 ThreadPoolExecutor 类似,ScheduledThreadPoolExecutor 也支持设置任务拒绝策略,如 AbortPolicyDiscardPolicy 等。

9. 总结

ScheduledThreadPoolExecutor 是 Java 中非常强大的定时任务调度工具,它继承了 ThreadPoolExecutor,并在其基础上提供了延时和周期性任务的执行功能。无论是单次延时任务,还是周期性任务,它都能够高效地进行调度和管理。掌握 ScheduledThreadPoolExecutor 的使用,可以帮助你更好地管理定时任务和周期性任务,适应多种实际应用场景。