这个 Demo 主要做两件事:
- 建一个
ScheduledThreadPoolExecutor(2),看看 2 线程时任务分配情况 - 对比
scheduleAtFixedRate和scheduleWithFixedDelay的行为 - 每个任务打印:当前时间、线程名、第几次执行
你可以新建一个ScheduledDemo.java直接运行:
import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.*; public class ScheduledDemo { private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("HH:mm:ss.SSS"); private static String now() { return LocalTime.now().format(F); } public static void main(String[] args) throws InterruptedException { // 2 个线程的定时线程池 ScheduledExecutorService ses = Executors.newScheduledThreadPool(2, new ThreadFactory() { private final ThreadFactory delegate = Executors.defaultThreadFactory(); private int idx = 1; @Override public Thread newThread(Runnable r) { Thread t = delegate.newThread(r); t.setName("sched-worker-" + (idx++)); return t; } }); System.out.println("[" + now() + "] main start"); // FixedRate 任务:理论上每 5 秒一次,task 执行 3 秒 Runnable fixedRateTask = new Runnable() { private int count = 0; @Override public void run() { String thread = Thread.currentThread().getName(); int curr = ++count; System.out.println("[" + now() + "] [FixedRate] 第 " + curr + " 次执行,线程 = " + thread); try { // 模拟执行 3 秒 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { System.out.println("[" + now() + "] [FixedRate] 被中断,线程 = " + thread); Thread.currentThread().interrupt(); } } }; // FixedDelay 任务:每次执行完后,延迟 5 秒再执行,task 执行 3 秒 Runnable fixedDelayTask = new Runnable() { private int count = 0; @Override public void run() { String thread = Thread.currentThread().getName(); int curr = ++count; System.out.println("[" + now() + "] [FixedDelay] 第 " + curr + " 次执行,线程 = " + thread); try { // 模拟执行 3 秒 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { System.out.println("[" + now() + "] [FixedDelay] 被中断,线程 = " + thread); Thread.currentThread().interrupt(); } } }; // 0 延时,之后每 5 秒触发一次(FixedRate) ses.scheduleAtFixedRate(fixedRateTask, 0, 5, TimeUnit.SECONDS); // 0 延时,执行完成之后等待 5 秒(FixedDelay) ses.scheduleWithFixedDelay(fixedDelayTask, 0, 5, TimeUnit.SECONDS); // 让 Demo 跑 40 秒,观察日志 TimeUnit.SECONDS.sleep(40); System.out.println("[" + now() + "] main shutdown"); ses.shutdown(); } }你跑一下,会看到类似(大概意思):
FixedRate:如果 3s < 5s,就基本是每 5 秒一轮
FixedDelay:永远是 “执行 3 秒 + 延迟 5 秒 = 8 秒一轮”
线程名会在
sched-worker-1/sched-worker-2之间分配,你能清楚看到:
同一个周期任务不会重叠执行,但是轮次之间可能换线程。
如果你想再观察“执行时间 > 间隔”的情况,把sleep(3)改成sleep(7),日志会更有意思:
FixedRate:会出现“补课式”紧接执行
FixedDelay:节奏变慢,但依旧“执行完 + 延时”。