ScheduledExecutorService 的 scheduleAtFixedRate 和 scheduleWithFixedDelay 有什么区别
java 线程池中使用 scheduledexecutorservice 来执行定时任务,核心区别在于 scheduleatfixedrate 按固定频率触发(基于上次开始时间),而 schedulewithfixeddelay 按固定延迟触发(基于上次结束时间)。要使用它们,需调用 scheduledexecutor.scheduleatfixedrate(runnable, initialdelay, period, timeunit) 或 scheduledexecutor.schedulewithfixeddelay(runnable, initialdelay, delay, timeunit)。若任务执行时间超过周期,前者会立即补跑一次,后者则顺延。

import java.util.concurrent.*;
public class ScheduleDemo {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Runnable task = () -> {
System.out.println("Task started at " + System.currentTimeMillis());
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Task finished at " + System.currentTimeMillis());
};
// 初始延迟 1s,每 3s 执行一次(固定频率)
executor.scheduleAtFixedRate(task, 1, 3, TimeUnit.SECONDS);
// 初始延迟 1s,每次结束后等待 3s 再执行(固定延迟)
executor.scheduleWithFixedDelay(task, 1, 3, TimeUnit.SECONDS);
}
}运行上述代码,scheduleAtFixedRate 会在第 1s、4s、7s... 启动任务,即使任务耗时 2s,下一次仍会在第 4s 启动(可能重叠)。scheduleWithFixedDelay 会在第 1s 启动,第 3s 结束,然后等待 3s,在第 6s 启动下一次。两者均返回 ScheduledFuture 对象,可用于取消任务。
ScheduledExecutorService 如何提交一次性定时任务
Java 线程池中提交一次性定时任务应使用 ScheduledExecutorService 的 schedule 方法。该方法接受一个 Runnable 或 Callable,以及初始延迟时间和时间单位。调用后,任务将在指定延迟后执行一次,不重复。例如 scheduledExecutor.schedule(Runnable, 5, TimeUnit.SECONDS) 会在 5 秒后执行一次任务。

立即学习“Java免费学习笔记(深入)”;
import java.util.concurrent.*;
public class OneTimeSchedule {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable oneTimeTask = () -> System.out.println("One-time task executed at " + System.currentTimeMillis());
// 延迟 2 秒后执行一次
ScheduledFuture<?> future = executor.schedule(oneTimeTask, 2, TimeUnit.SECONDS);
// 可选:检查是否已取消或完成
if (!future.isDone()) {
System.out.println("Task is pending...");
}
}
}该程序在 2 秒后打印一次时间戳。schedule 返回的 ScheduledFuture 允许通过 cancel(true) 取消任务。若任务抛出异常,异常会被封装在 ScheduledFuture.get() 中抛出,但不会中断线程池。此方法适用于需要精确控制单次执行时间的场景,如延迟初始化或重试机制。
当 scheduleAtFixedRate 的任务执行时间超过周期时会发生什么
当 scheduleAtFixedRate 的任务执行时间超过设定的周期时,下一次任务的启动时间会基于上一次任务的开始时间计算,导致任务可能连续执行或重叠。例如,周期为 3 秒,任务耗时 4 秒,则第一次在 t=0 开始,t=4 结束;第二次应在 t=3 开始,但由于 t=3 时任务仍在运行,线程池会等待当前任务完成后立即启动下一次,即在 t=4 启动第二次任务。这可能导致任务堆积或并发执行。

import java.util.concurrent.*;
public class OverlapDemo {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Runnable longTask = () -> {
System.out.println("Start: " + System.currentTimeMillis());
try { Thread.sleep(4000); } catch (InterruptedException e) {}
System.out.println("End: " + System.currentTimeMillis());
};
// 周期 3 秒,任务耗时 4 秒
executor.scheduleAtFixedRate(longTask, 0, 3, TimeUnit.SECONDS);
}
}运行此代码,你会看到任务在 t=0 开始,t=4 结束;紧接着在 t=4 再次开始,t=8 结束。由于线程池大小为 2,两个任务可以并行执行,但若线程池大小为 1,则必须等待前一个任务完成后才能启动下一个,导致实际间隔变为任务耗时而非设定周期。这种行为在需要严格间隔的场景下应避免,可改用 scheduleWithFixedDelay 或手动管理调度逻辑。


















