Timers
Three classes, two of them called Timer, and the difference matters.
For a Swing interface
Timer timer = new javax.swing.Timer(1000, e -> label.setText(now()));
timer.start();
javax.swing.Timer fires on the Event Dispatch Thread, so the listener may touch components directly. That is exactly what you want for a clock, an animation or a periodic refresh.
The corollary: the listener must be quick. It runs on the thread that paints the interface, so slow work there freezes the window.
For background work
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(this::poll, 0, 5, TimeUnit.SECONDS);
Runs on its own thread. Preferred over java.util.Timer, which is the older class and has two flaws worth knowing: it uses a single thread for all tasks, so one long task delays the rest, and an uncaught exception in a task kills the timer thread and silently stops everything else scheduled on it.
ScheduledExecutorService survives a failing task. Shut it down when you are finished:
exec.shutdown();
Otherwise its thread keeps the JVM alive.
Fixed rate or fixed delay
scheduleAtFixedRate(task, 0, 5, SECONDS); // every 5s from the start
scheduleWithFixedDelay(task, 0, 5, SECONDS); // 5s after each finishes
Fixed rate tries to keep to a schedule and will run tasks back to back to catch up if one overruns. Fixed delay always leaves the gap. For polling something, fixed delay is usually what you want; for a clock, fixed rate.
If it must touch the UI
A background timer’s task runs off the EDT, so it may not touch components:
exec.scheduleWithFixedDelay(() -> {
var data = fetch();
SwingUtilities.invokeLater(() -> table.setModel(new Model(data)));
}, 0, 30, SECONDS);
Fetch on the background thread, update inside invokeLater. Skipping the marshal produces intermittent painting faults that are very hard to reproduce.
Accuracy
None of these are real-time. A one-second timer fires approximately every second; scheduling delay, garbage collection and system load all add jitter. For animation, do not accumulate ticks to track elapsed time — read the clock:
long elapsed = System.nanoTime() - start;
Counting ticks drifts. Reading the clock does not.
Cancelling
swingTimer.stop();
scheduledFuture.cancel(false);
A timer nobody cancels keeps its target object reachable, which is a common and quiet memory leak in long-running applications.