What does Executors.newScheduledThreadPool provide, and why is it generally preferred over java.util.Timer?
answer
- schedule (once) / scheduleAtFixedRate (period from start) / scheduleWithFixedDelay (gap after finish)
- Timer = 1 thread; pool = n threads
- Timer task exception kills ALL tasks; pool isolates
- Timer uses wall-clock; pool uses relative/monotonic time
- Still unbounded DelayedWorkQueue — catch exceptions in repeating tasks
basics
~20 snewScheduledThreadPool returns a pool that can run tasks after a delay or repeatedly on a schedule, using multiple threads. It's preferred over Timer because Timer uses a single thread and one task that throws an exception or runs long can break all the others.
solid answer
~40 sExecutors.newScheduledThreadPool(n) returns a ScheduledExecutorService backed by n threads. It adds schedule(task, delay, unit) for one-shot delayed execution and two repeating modes: scheduleAtFixedRate (next run starts a fixed period after the previous start, so runs can bunch up if tasks run long) and scheduleWithFixedDelay (next run starts a fixed gap after the previous finishes). It is preferred over java.util.Timer for three reasons: Timer uses a single thread, so one long task delays all others, whereas the scheduled pool can run several concurrently; an unchecked exception in a Timer task kills the Timer thread and cancels all remaining tasks, while the executor isolates failures per task; and Timer is sensitive to wall-clock changes because it schedules on absolute time, whereas ScheduledThreadPoolExecutor uses relative/monotonic timing. The scheduled executor also returns a ScheduledFuture you can cancel and inspect.
code
java · 10 linesScheduledExecutorService s = Executors.newScheduledThreadPool(2);
// run once after 5s
s.schedule(() -> System.out.println("late"), 5, TimeUnit.SECONDS);
// fixed delay: 30s gap between the end of one run and start of next
s.scheduleWithFixedDelay(() -> {
try { doWork(); }
catch (Exception e) { /* swallow so future runs continue */ }
}, 0, 30, TimeUnit.SECONDS);go deeper
Know that the scheduled pool can run tasks after a delay or repeatedly, and that it's safer than Timer because Timer's single thread and exceptions can break everything.
Distinguish the three scheduling methods (especially fixed-rate vs fixed-delay) and list the concrete reasons (multi-thread, failure isolation, monotonic timing) the scheduled pool beats Timer.
Explain catch-up behaviour and non-overlap of fixed-rate, that an uncaught exception suppresses a periodic task's future runs, the unbounded DelayedWorkQueue caution, and how to size n.
Weigh in-process scheduling vs a distributed scheduler (Quartz, cron, dedicated job system) for reliability, clustering, and missed-run semantics; define guarantees (at-most-once, catch-up) the team needs.
## What scheduling means here Sometimes you don't want a task to run *now* but **after a delay** (run in 5 seconds) or **periodically** (run every minute). Java's `ScheduledExecutorService` interface extends `ExecutorService` with three methods for this, and `Executors.newScheduledThreadPool(n)` is the factory that gives you an implementation backed by `n` worker threads. ## The three scheduling methods - **`schedule(task, delay, unit)`** — run the task **once**, after `delay` time units. Returns a `ScheduledFuture` (a `Future` you can cancel or query). - **`scheduleAtFixedRate(task, initialDelay, period, unit)`** — run repeatedly; each execution is scheduled to **start `period` after the previous execution's *start*time**. If a run takes longer than `period`, subsequent runs don't overlap (the executor won't run two at once for the same task) but they queue up and fire back-to-back to "catch up". - **`scheduleWithFixedDelay(task, initialDelay, delay, unit)`** — run repeatedly; each execution starts `delay` after the previous one **finishes**. This guarantees a fixed *gap* between runs regardless of how long each takes — usually the safer choice for jobs of variable duration. The difference between the two repeating modes matters: *fixed rate* tries to keep a steady cadence (good for sampling at exact intervals); *fixed delay* keeps a steady idle gap (good for polling where you don't want overlap or pile-up). ## The old way: java.util.Timer `java.util.Timer` (with `TimerTask`) is the pre-`java.util.concurrent` scheduling tool. It has several well-known defects that `ScheduledThreadPoolExecutor` fixes: 1. **Single thread.** A `Timer` uses exactly one background thread. If one task runs long, **every other scheduled task is delayed** behind it. A scheduled pool with `n > 1` runs tasks concurrently, so a slow task doesn't starve the rest. 2. **No failure isolation.** If a `TimerTask` throws an **unchecked exception** (a `RuntimeException`/`Error`), it **kills the Timer's thread**, and *all* remaining scheduled tasks are silently cancelled — the whole Timer is dead. With `ScheduledThreadPoolExecutor`, an exception is confined to that one execution; the pool and other tasks keep going. (Note: for a *repeating* scheduled task, an uncaught exception suppresses *future runs of that same task*, so you should still catch inside the task — but it does not take down the pool or other tasks.) 3. **Absolute-time sensitivity.** `Timer` schedules against the **system wall-clock**, so if the OS clock is changed (NTP adjustment, manual change, DST), timings can misfire. `ScheduledThreadPoolExecutor` schedules on **relative delays from a monotonic source**, so it is immune to wall-clock jumps. ## Why this is the recommended tool Because of multi-threading, per-task failure isolation, monotonic timing, and the richer `ScheduledFuture` return (cancel, check done/delay), the consensus (including *Effective Java*) is: **use `ScheduledThreadPoolExecutor` / `Executors.newScheduledThreadPool`, not `Timer`.** ## The hazard it still shares It is built on a `ThreadPoolExecutor` whose work queue (a `DelayedWorkQueue`) is **unbounded**, so the same backlog-OOM caution from the other factories applies if you schedule far more tasks than `n` threads can drain. Size `n` for the expected concurrency, keep periodic tasks short, and always catch exceptions inside repeating tasks so a single failure doesn't silently stop future runs. ## Example ```java ScheduledExecutorService s = Executors.newScheduledThreadPool(2); s.scheduleWithFixedDelay( () -> { try { poll(); } catch (Exception e) { log.warn("poll failed", e); } }, 0, 30, TimeUnit.SECONDS); // run, then 30s gap, repeat ``` Wrapping the body in try/catch keeps a transient failure from cancelling all future runs of this periodic task.
- What is the difference between scheduleAtFixedRate and scheduleWithFixedDelay?Fixed rate measures the period from the START of each run, aiming for a steady cadence (runs can bunch up to catch up if a task overran). Fixed delay measures from the END of each run, guaranteeing a constant idle gap between runs regardless of how long each takes. Fixed delay is safer for variable-duration jobs that shouldn't pile up.
- What happens if a repeating scheduled task throws an uncaught exception?That task's future executions are silently suppressed (it stops repeating), but the pool and other tasks keep running. So you should catch exceptions inside the task body to keep it alive. This is still far better than Timer, where one exception kills the single thread and cancels every scheduled task.
Timer is a single night watchman with one clipboard: if he trips and falls (an exception), every round on his list goes unchecked. The scheduled pool is a team of watchmen — one stumbling doesn't cancel everyone else's rounds.
saying these in an interview costs you the question
- Saying Timer is multi-threaded (it has exactly one thread)
- Claiming an exception in a scheduled task takes down the whole pool (it only suppresses that task's future runs)
- Confusing fixed-rate (from start) with fixed-delay (from finish)
- Forgetting the scheduled pool's queue is also unbounded