在 Java 並發編程中,延遲執行與定時排程是常見需求。JDK 5.0 引入的 java.util.concurrent 套件提供了兩種核心方案:DelayQueue 適合構建自定義的延遲任務隊列,而 ScheduledThreadPoolExecutor 則是開箱即用的排程執行器。本文深入解析兩者原理與實戰代碼。
DelayQueue 是無界阻塞隊列,只能放入實現 Delayed 介面的物件:
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit); // 返回剩餘延遲時間
}
隊列頭部永遠是「延遲期滿時間最早」的元素。當 getDelay(NANOSECONDS) <= 0 時,元素視為到期可取出。
設計一個 DelayedTasker 管理任務隊列,內部類 DelayedTask 同時實現 Delayed 與 Runnable:
public class DelayedTasker implements Runnable {
private final DelayQueue<DelayedTask> queue = new DelayQueue<>();
public void addTask(DelayedTask task) { queue.put(task); }
public void removeTask() { queue.poll(); }
@Override public void run() {
while (!queue.isEmpty()) {
try { queue.take().run(); } // 取出並執行到期任務
catch (InterruptedException e) { break; }
}
System.out.println("Finished DelayedTask");
}
public static class DelayedTask implements Delayed, Runnable {
private static int counter = 0;
private final int id = counter++;
private final int delta; // 延遲秒數
private final long trigger; // 觸發時間戳(納秒)
public DelayedTask(int delayInSeconds) {
delta = delayInSeconds;
trigger = System.nanoTime() + TimeUnit.NANOSECONDS.convert(delta, TimeUnit.SECONDS);
}
@Override public long getDelay(TimeUnit unit) {
return unit.convert(trigger - System.nanoTime(), TimeUnit.NANOSECONDS);
}
@Override public int compareTo(Delayed o) {
long diff = trigger - ((DelayedTask)o).trigger;
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
@Override public void run() { System.out.println(this); }
@Override public String toString() { return "[" + delta + "s]Task" + id; }
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
DelayedTasker tasker = new DelayedTasker();
Random rand = new Random();
for (int i = 0; i < 10; i++) tasker.addTask(new DelayedTask(rand.nextInt(5)));
exec.execute(tasker);
exec.shutdown();
}
}
輸出範例(按延遲時間排序執行):
[0s]Task 1
[0s]Task 2
[0s]Task 3
[1s]Task 6
[2s]Task 5
[3s]Task 8
[4s]Task 0
[4s]Task 4
[4s]Task 7
[4s]Task 9
Finished DelayedTask
特點:任務邏輯封裝在 DelayedTask.run(),隊列長度無上限,適合「任務型別固定、需自定義排序」的場景。
ScheduledThreadPoolExecutor 繼承自 ThreadPoolExecutor,實現 ScheduledExecutorService,支援一次性延遲執行與週期性執行。
schedule(Runnable, delay, unit):一次性延遲執行。scheduleAtFixedRate(Runnable, initialDelay, period, unit):固定頻率週期執行。scheduleWithFixedDelay(Runnable, initialDelay, delay, unit):固定延遲週期執行(上次結束到下次開始)。public class ScheduleTasker {
private final ScheduledThreadPoolExecutor scheduler;
public ScheduleTasker() { this(10); }
public ScheduleTasker(int corePoolSize) {
scheduler = new ScheduledThreadPoolExecutor(corePoolSize);
}
public void schedule(Runnable task, long delaySeconds) {
scheduler.schedule(task, delaySeconds, TimeUnit.SECONDS);
}
public void shutdown() { scheduler.shutdown(); }
public static void main(String[] args) {
ScheduleTasker tasker = new ScheduleTasker();
tasker.schedule(() -> System.out.println("[1s]Task 1"), 1);
tasker.schedule(() -> System.out.println("[2s]Task 2"), 2);
tasker.schedule(() -> System.out.println("[4s]Task 3"), 4);
tasker.schedule(() -> System.out.println("[10s]Task 4"), 10);
tasker.shutdown();
}
}
輸出:
[1s]Task 1
[2s]Task 2
[4s]Task 3
[10s]Task 4
優勢:線程池管理、異常處理、取消機制、週期執行等開箱即用,生產環境首選。
| 維度 | DelayQueue | ScheduledThreadPoolExecutor |
|---|---|---|
| 適用場景 | 自定義任務隊列、特殊排序邏輯 | 標準定時/週期任務、生產環境 |
| 任務邏輯 | 需預先定義在 DelayedTask 中 | 可動態傳入任意 Runnable |
| 線程管理 | 自行管理消費者線程 | 內建線程池,自動調度 |
| 週期執行 | 需自行實現 | 原生支援 |
總結:簡單排程用 ScheduledThreadPoolExecutor;需完全控制隊列行為時用 DelayQueue。