Implementing Scheduled Tasks in Java: Timer, ScheduledExecutorService, Spring Task, and Distributed Approaches
This article explains several ways to implement scheduled tasks in Java, covering the simple Timer class, the more robust ScheduledExecutorService, Spring's @Scheduled annotation, and distributed solutions using Redis ZSet and key‑space notifications, with code examples and practical considerations.
Scheduled tasks are common in real‑world development, such as automatically canceling unpaid orders after 30 minutes or performing nightly data aggregation. This article reviews the simplest implementations and their trade‑offs.
TOP 1: Timer
Timer is a JDK‑provided class that can be used directly in any project. Below is a basic implementation.
public class MyTimerTask {
public static void main(String[] args) {
// Define a task
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("Run timerTask:" + new Date());
}
};
// Timer instance
Timer timer = new Timer();
// Schedule task (delay 1s, repeat every 3s)
timer.schedule(timerTask, 1000, 3000);
}
}Running the program produces output similar to:
Run timerTask:Mon Aug 17 21:29:25 CST 2020 Run timerTask:Mon Aug 17 21:29:28 CST 2020 Run timerTask:Mon Aug 17 21:29:31 CST 2020
Timer drawbacks
When a task runs longer than its scheduled interval, it blocks other tasks, and an exception in one task terminates the whole timer thread.
Issue 1: Long‑running task delays others
The following code demonstrates two tasks where the first sleeps for 5 seconds.
public class MyTimerTask {
public static void main(String[] args) {
// Task 1
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("进入 timerTask 1:" + new Date());
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Run timerTask 1:" + new Date());
}
};
// Task 2
TimerTask timerTask2 = new TimerTask() {
@Override
public void run() {
System.out.println("Run timerTask 2:" + new Date());
}
};
Timer timer = new Timer();
timer.schedule(timerTask, 1000, 3000);
timer.schedule(timerTask2, 1000, 3000);
}
}Because task 1 takes 5 seconds, task 2 is also delayed, stretching its interval from 3 seconds to 10 seconds.
Issue 2: Exception in a task stops others
If a task throws an exception, the timer thread terminates and no further tasks run.
public class MyTimerTask {
public static void main(String[] args) {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("进入 timerTask 1:" + new Date());
int num = 8 / 0; // simulate exception
System.out.println("Run timerTask 1:" + new Date());
}
};
TimerTask timerTask2 = new TimerTask() {
@Override
public void run() {
System.out.println("Run timerTask 2:" + new Date());
}
};
Timer timer = new Timer();
timer.schedule(timerTask, 1000, 3000);
timer.schedule(timerTask2, 1000, 3000);
}
}The exception aborts the timer thread, preventing task 2 from executing.
Timer summary
Timer is convenient but unsuitable for production when tasks may run long or throw exceptions.
TOP 2: ScheduledExecutorService
Introduced in JDK 1.5, ScheduledExecutorService provides all Timer features and solves its problems.
public class MyScheduledExecutorService {
public static void main(String[] args) {
// Create a thread pool with 10 threads
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// Execute a task every 3 seconds after an initial 1‑second delay
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("Run Schedule:" + new Date());
}, 1, 3, TimeUnit.SECONDS);
}
}Output:
Run Schedule:Mon Aug 17 21:44:23 CST 2020 Run Schedule:Mon Aug 17 21:44:26 CST 2020 Run Schedule:Mon Aug 17 21:44:29 CST 2020
Reliability tests
1️⃣ Long‑running task
Two tasks are scheduled; the first sleeps for 5 seconds. The second continues to run on schedule, showing no interference.
public class MyScheduledExecutorService {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// Task 1 (long running)
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("进入 Schedule:" + new Date());
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Run Schedule:" + new Date());
}, 1, 3, TimeUnit.SECONDS);
// Task 2 (normal)
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("Run Schedule2:" + new Date());
}, 1, 3, TimeUnit.SECONDS);
}
}The results confirm that task 2 is unaffected by the delay of task 1.
2️⃣ Exception handling
When task 1 throws an exception, task 2 still runs normally.
public class MyScheduledExecutorService {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// Task 1 (throws exception)
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("进入 Schedule:" + new Date());
int num = 8 / 0; // exception
System.out.println("Run Schedule:" + new Date());
}, 1, 3, TimeUnit.SECONDS);
// Task 2
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("Run Schedule2:" + new Date());
}, 1, 3, TimeUnit.SECONDS);
}
}Task 2 continues to execute despite the exception in task 1.
ScheduledExecutorService summary
For single‑machine production environments, ScheduledExecutorService is the recommended choice because it isolates tasks from each other's runtime issues.
TOP 3: Spring Task
When using Spring or Spring Boot, you can leverage the framework’s built‑in scheduling support, which also allows cron‑style expressions for complex schedules such as “every Friday at 23:59:59”.
1️⃣ Enable scheduling
@SpringBootApplication
@EnableScheduling // enable scheduling
public class DemoApplication {
// ...
}2️⃣ Add a scheduled method
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class TaskUtils {
@Scheduled(cron = "59 59 23 0 0 5") // every Friday 23:59:59
public void doTask() {
System.out.println("我是定时任务~");
}
}The task runs automatically after the application starts.
Cron expression basics
A cron expression consists of 6 (or 7) fields separated by spaces. The article includes two illustrative images and a link to an online generator.
Knowledge extension: Distributed scheduled tasks
For distributed environments, Redis can be used. Two common patterns are presented.
① ZSet implementation
Tasks are stored in a sorted set with the execution timestamp as the score. A loop polls the set each second and processes due tasks.
import redis.clients.jedis.Jedis;
import utils.JedisUtils;
import java.time.Instant;
import java.util.Set;
public class DelayQueueExample {
private static final String _KEY = "myTaskQueue";
public static void main(String[] args) throws InterruptedException {
Jedis jedis = JedisUtils.getJedis();
long delayTime = Instant.now().plusSeconds(30).getEpochSecond();
jedis.zadd(_KEY, delayTime, "order_1");
// add more test data ...
doDelayQueue(jedis);
}
public static void doDelayQueue(Jedis jedis) throws InterruptedException {
while (true) {
Instant nowInstant = Instant.now();
long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond();
long nowSecond = nowInstant.getEpochSecond();
Set
data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);
for (String item : data) {
System.out.println("消费:" + item);
}
jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);
Thread.sleep(1000);
}
}
}② Key‑space notifications
Enable Redis key‑space events (config set notify-keyspace-events Ex) and subscribe to expiration messages to trigger tasks.
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import utils.JedisUtils;
public class TaskExample {
public static final String _TOPIC = "__keyevent@0__:expired";
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedis();
doTask(jedis);
}
public static void doTask(Jedis jedis) {
jedis.psubscribe(new JedisPubSub() {
@Override
public void onPMessage(String pattern, String channel, String message) {
System.out.println("收到消息:" + message);
}
}, _TOPIC);
}
}The article concludes with a set of promotional links and a QR code for the author’s contact.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.